JDK-8088279 : Jerky animations
  • Type: Bug
  • Component: javafx
  • Sub-Component: animation
  • Affected Version: 8,9
  • Priority: P3
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2014-05-06
  • 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 :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
When building a simple Pong game with JavaFX, I noticed that even the simplest animation is jerky. The test case below should illustrate that:

public class AnimationTest extends Application
{
    private static final double WIDTH = 1000;
    private static final double HEIGHT = 600;
    private static final double SIZE = 10;
        
    @Override
    public void start(Stage stage)
    {
        Rectangle rectangle = new Rectangle(SIZE, SIZE);
        rectangle.setTranslateX(0);
        rectangle.setTranslateY((HEIGHT - SIZE) / 2);
        
        Scene scene = new Scene(new Group(rectangle), WIDTH, HEIGHT);
        stage.setScene(scene);
        stage.show();
        
        new AnimationTimer()
        {
            private boolean goingRight = true;
            
            @Override
            public void handle(long now)
            {   
                rectangle.setTranslateX(rectangle.getTranslateX() + (goingRight ? 3 : -3));
                if (rectangle.getTranslateX() > WIDTH - SIZE) {
                    rectangle.setTranslateX(WIDTH - SIZE);
                    goingRight = false;
                } else if (rectangle.getTranslateX() < 0) {
                    rectangle.setTranslateX(0);
                    goingRight = true;
                }
            }
        }.start();
    }
    
    public static void main(String... args)
    {
        Application.launch(AnimationTest.class, args);
    }
}

If I add frame measuring to the AnimationTimer, I notice that most frames last about 16ms, but once in a while a frame comes along that lasts anywhere from 30ms up to 120ms. When running this test case with pulse logger on, I noticed similar issues, for example:

PULSE: 144 [17ms:31ms]
T12 (0 +0ms): CSS Pass
T12 (0 +0ms): Layout Pass
T12 (0 +0ms): Waiting for previous rendering
T12 (0 +0ms): Copy state to render graph
T10 (0 +0ms): Dirty Opts Computed
T10 (0 +1ms): Render Roots Discovered
T10 : 1 different dirty regions to render
T10 : Dirty Region 0: RectBounds { minX:429.0, minY:295.0, maxX:442.0, maxY:305.0} (w:13.0, h:10.0)
T10 : Render Root Path 0: [com.sun.javafx.sg.prism.NGGroup@6d1692fd]
T10 (1 +0ms): Painted
T10 (1 +30ms): Presentable.present
T10 (31 +0ms): Finished Presenting Painter
Counters:
	Nodes rendered: 2
	Nodes visited during render: 2

[145 17ms:15ms][146 16ms:1ms][147 17ms:2ms][148 17ms:13ms][149 16ms:7ms][150 17ms:5ms][151 16ms:2ms][152 18ms:1ms][153 16ms:2ms][154 16ms:2ms][155 17ms:2ms][156 17ms:2ms][157 16ms:1ms][158 17ms:1ms][159 17ms:1ms][160 17ms:0ms][161 16ms:3ms][162 17ms:2ms][163 17ms:2ms][164 17ms:0ms]
[165 16ms:1ms][166 16ms:1ms][167 17ms:1ms][168 17ms:2ms][169 16ms:7ms][170 17ms:4ms][171 17ms:2ms][172 16ms:3ms][173 17ms:2ms][174 17ms:1ms][175 17ms:1ms][176 17ms:1ms][177 17ms:2ms][178 15ms:2ms][179 17ms:2ms][180 17ms:2ms][181 16ms:1ms][182 17ms:1ms][183 17ms:2ms][184 16ms:2ms]
[185 17ms:2ms][186 17ms:2ms][187 16ms:3ms][188 17ms:2ms][189 17ms:2ms][190 16ms:2ms][191 17ms:2ms][192 17ms:2ms][193 16ms:3ms][194 17ms:2ms][195 17ms:3ms][196 17ms:7ms][197 16ms:2ms][198 17ms:2ms][199 17ms:2ms][200 16ms:2ms][201 17ms:0ms][202 16ms:1ms][203 17ms:1ms][204 17ms:1ms]
[205 16ms:2ms][206 17ms:2ms][207 17ms:1ms][208 16ms:3ms][209 17ms:2ms][210 17ms:3ms][211 16ms:2ms][212 18ms:15ms][213 16ms:0ms][214 17ms:2ms][215 16ms:2ms][216 17ms:1ms][217 16ms:2ms][218 18ms:2ms][219 16ms:2ms][220 18ms:2ms][221 16ms:2ms][222 16ms:2ms][223 17ms:2ms][224 16ms:2ms][225 17ms:0ms][226 16ms:1ms][227 17ms:2ms][228 18ms:3ms][229 17ms:0ms][230 15ms:3ms][231 17ms:2ms][232 17ms:2ms][233 16ms:3ms][234 18ms:0ms][235 15ms:2ms][236 17ms:2ms][237 17ms:2ms][238 16ms:1ms][239 17ms:1ms][240 17ms:2ms][241 16ms:2ms][242 17ms:2ms][243 17ms:3ms][244 16ms:1ms]
[245 18ms:1ms][246 16ms:1ms][247 16ms:2ms][248 17ms:2ms][249 17ms:2ms][250 16ms:6ms][251 19ms:1ms][252 15ms:2ms][253 18ms:2ms][254 15ms:6ms][255 17ms:2ms][256 16ms:3ms]

PULSE: 257 [18ms:30ms]
T12 (0 +0ms): CSS Pass
T12 (0 +0ms): Layout Pass
T12 (0 +0ms): Waiting for previous rendering
T12 (0 +0ms): Copy state to render graph
T10 (0 +0ms): Dirty Opts Computed
T10 (0 +0ms): Render Roots Discovered
T10 : 1 different dirty regions to render
T10 : Dirty Region 0: RectBounds { minX:768.0, minY:295.0, maxX:781.0, maxY:305.0} (w:13.0, h:10.0)
T10 : Render Root Path 0: [com.sun.javafx.sg.prism.NGGroup@6d1692fd]
T10 (1 +0ms): Painted
T10 (1 +29ms): Presentable.present
T10 (30 +0ms): Finished Presenting Painter
Counters:
	Nodes rendered: 2
	Nodes visited during render: 2

And so forth...

RT-13974 seems to be similar.
Comments
See JDK-8134837 for additional information.
15-08-2016

Aug 11, 2016: pending Assignee's evaluation
11-08-2016

Additional information can be found at JDK-8135140 and JDK-8135143. On Windows, according to the submitter of JDK-8135140: 7u79 : animation is good 7u79 with -Dprism.vsync=false : animation is jerky 8 : animation is jerky 8 with -Djavafx.animation.fullspeed=true : animation is good It looks like there is some behavior change in 8 from 7.
07-09-2015

See JDK-8133843 and JDK-8133844 for additional comments on this issue.
18-08-2015

We are unlikely to get to this for 8u40, especially since it isn't consistently reproducible for us.
17-09-2014

I do see somewhat jerky animation on my Linux box, and in reading the comments, the reporter is able to see it on Windows as well.
02-06-2014

I cannot reproduce this problem on either my WIndows or my Mac machine. It's possible it is system dependent (I don't have a retina display on my Mac). Moving to 8u40 (we are out of time for P4 bugs anyway).
02-06-2014

Steve, any reason to assign this back to me? I already evaluated the bug and it's probably some pipeline issue, so I assigned that to Kevin.
15-05-2014

I was able to reproduce this issue on a Windows machine with JDK 8u5. I do not notice it with JDK 7u45, not on my Mac and not on the Windows machine. This was using the TranslateTransition example.
07-05-2014

Whatever is causing the long pulse, it's in Presentable.present call. I don't see jerky animations on Linux (which also has ES2 pipeline as Mac does), but it may be something Mac specific. As Kevin already mentioned, the way you use AnimationTimer is wrong, but the TranslateTransition should should be without (too many) jumps when nothing else is happening in the app. Anyway, this is not an Animation issue, so I'm assigning it to graphics.
07-05-2014

FYI: This is how I actually use the AnimationTimer: http://asipofjava.blogspot.be/2014/05/game-loops-and-applying-them-to-javafx.html. Any feedback is greatly appreciated, as I just learn as I go.
06-05-2014

Hi Kevin, This is just an extremely simplified example. I use AnimationTimer for all sorts of game loops, so that's why it's still in the example. But I have the exact same issue if I replace it with a TranslateTransition: public class AnimationTest extends Application { private static final double WIDTH = 1000; private static final double HEIGHT = 600; private static final double SIZE = 10; @Override public void start(Stage stage) { Rectangle rectangle = new Rectangle(SIZE, SIZE); rectangle.setTranslateX(0); rectangle.setTranslateY((HEIGHT - SIZE) / 2); Scene scene = new Scene(new Group(rectangle), WIDTH, HEIGHT); stage.setScene(scene); stage.show(); TranslateTransition transition = new TranslateTransition(); transition.setNode(rectangle); transition.setFromX(0); transition.setToX(WIDTH - SIZE); transition.setDuration(Duration.seconds((WIDTH / SIZE) / 80)); // 80px/s transition.setAutoReverse(true); transition.setCycleCount(Animation.INDEFINITE); transition.play(); } public static void main(String... args) { Application.launch(AnimationTest.class, args); } } The jerkyness remains and I still get pulses like this: PULSE: 69 [16ms:140ms] T12 (0 +0ms): CSS Pass T12 (0 +0ms): Layout Pass T12 (0 +0ms): Waiting for previous rendering T12 (0 +0ms): Copy state to render graph T10 (0 +1ms): Dirty Opts Computed T10 (1 +0ms): Render Roots Discovered T10 : 1 different dirty regions to render T10 : Dirty Region 0: RectBounds { minX:856.0, minY:295.0, maxX:887.0, maxY:305.0} (w:31.0, h:10.0) T10 : Render Root Path 0: [com.sun.javafx.sg.prism.NGGroup@130cf033] T10 (1 +0ms): Painted T10 (1 +139ms): Presentable.present T10 (140 +0ms): Finished Presenting Painter Counters: Nodes rendered: 2 Nodes visited during render: 2
06-05-2014

Having said that, the fact that some frames are taking 30 - 120 ms is worth looking into, but you should compute your animation so that they are not relying on each frame being a constant time.
06-05-2014

AnimationTimer isn't meant to be used the way you are using it, and will lead to jerky animation. You will not have a constant rate of change over time by using a fixed per-frame delta. You should look at either using the Transition classes, or using a Timeline and KeyFrame / KeyValue. Assigning to Martin in case he wants to provide more information, but based on your example code, this is not a bug.
06-05-2014