JavaFx animation poor performance, consumes all my

2019-01-22 00:50发布

问题:

I have written the following demonstration program. Four HBox(s) containing one Text node each, are added to the root (Group). The first and last are animated with a timeline to swap their positions. All the HBox(s) have the same css style.

The result is a very low 'frames per second' amimation. I have an dual core E7400 2.8Ghz Cpu. The one core was used 100%. I expected the computations to be done in GPU instead. After removing most of the css (especially the shadow effect) the animation went smoother. Is there something I can do in order to preserve the visual effects while I have a better performance? Is it considered a bad practice to use css to decorate nodes that are going to be animated?

I also used the -Dprism.verbose=true to check if the hardware accelaration is on. Everything seems ok

Prism pipeline init order: d3d sw
Using platform text rasterizer
Using native-based Pisces rasterizer
Using dirty region optimizations
Not using texture mask for primitives
Not forcing power of 2 sizes for textures
Using hardware CLAMP_TO_ZERO mode
Opting in for HiDPI pixel scaling
Prism pipeline name = com.sun.prism.d3d.D3DPipeline
Loading D3D native library ...
        succeeded.
D3DPipelineManager: Created D3D9 device
Direct3D initialization succeeded
(X) Got class = class com.sun.prism.d3d.D3DPipeline
Initialized prism pipeline: com.sun.prism.d3d.D3DPipeline
Maximum supported texture size: 8192
Maximum texture size clamped to 4096
OS Information:
        Windows 7 build 7601
D3D Driver Information:
    ATI Radeon HD 4800 Series
    \\.\DISPLAY1
    Driver aticfx32.dll, version 8.17.10.1129
    Pixel Shader version 3.0
    Device : ven_1002, dev_9440, subsys_0502174B
    Max Multisamples supported: 4
 vsync: true vpipe: true
Loading Prism common native library ...
        succeeded.

and here is the program

public class Sample extends Application{

    public void start(Stage primaryStage) throws Exception {
        Group root = new Group();
        Card card1 = new Card(1);
        Card card2 = new Card(2);
        Card card3 = new Card(3);
        Card card4 = new Card(3);
        card1.relocate(100, 200);
        card2.relocate(250, 200);
        card3.relocate(400, 200);
        card4.relocate(550, 200);
        root.getChildren().add(card1);
        root.getChildren().add(card2);
        root.getChildren().add(card3);
        root.getChildren().add(card4);
        primaryStage.setScene(new Scene(root , 800, 600, Color.DARKSLATEGREY));
        primaryStage.show();

        Timeline tl = new Timeline(new KeyFrame(
                Duration.millis(500),
                new KeyValue(card1.layoutXProperty(), card4.getLayoutX()),
                new KeyValue(card4.layoutXProperty(), card1.getLayoutX())
                ));
        tl.setAutoReverse(true);
        tl.setCycleCount(Timeline.INDEFINITE);
        tl.play();
    }

    class Card extends HBox{

        private static final String textStyle =
                "-fx-fill: linear-gradient(BROWN, WHITE);"+
                "-fx-font-size: 100px;"+
                "-fx-font-weight: BOLD;"+
                "-fx-stroke-type: outside;"+
                "-fx-stroke-width: 3;"+
                "-fx-stroke: linear-gradient(WHITE, BROWN);"+
                "-fx-blend-mode: hard-light;";

        boolean active;

        public final Text text;

        public Card(int number) {
            setStyle("-fx-effect: dropshadow(one-pass-box, black, 30, 0, 10, 20);");
            text = new Text(""+number);
            text.setStyle(textStyle);
            getChildren().add(text);
        }
    }

    public static void main(String[] args) {
        launch(args);
    }

}

回答1:

  1. Cache the node (doing this with your sample dropped the CPU usage from 100% of a core to 1%).

    setCache(true);
    setCacheShape(true);
    setCacheHint(CacheHint.SPEED);
    
  2. Read the javadoc on node caching.

  3. Define your CSS rules in a stylesheet and not inline styles (if I recall that is more efficient due to the nature of the CSS implementation in JavaFX).

  4. Take a look at performance tips and tricks on the open-jfx wiki.

  5. Read related questions:

    • JavaFX 2 drawing performance
    • What is the best way to display millions of images in Java?
    • How do setCache() and CacheHint work together in JavaFX?

I expected the computations to be done in GPU instead.

JavaFX will do lots of computations on the GPU, however it does a tradeoff - some computations are better done on a CPU and JavaFX will use the CPU for these kinds of computations. Your issue is not one of having computations done on the GPU or CPU. Your issue is having too many computations done because appropriate caching hints have not been provided to the JavaFX system.

Is it considered a bad practice to use css to decorate nodes that are going to be animated?

Inline css styles are bad practice in general - put the css in a stylesheet. Using CSS on animated nodes is fine for most use cases.

Indeed setting setCache(true) and setCacheHint(CacheHint.SPEED) improved the animation but it is still laggy.

The animation does not lag on my machine, but then it is not really comparable because the hardware and software in my machine is from 2014, not 2008.

Perhaps try updating your graphics drivers and updating JavaFX to the latest development build.

Also, slow down the animation (e.g. give it a duration of five seconds rather than half a second). It can be hard to visually detect the smoothness of an animation when it moves very quickly.

Log a bug report in the JavaFX issue tracker - the JavaFX developers should be able to supply additional information on how to do turn on detailed JavaFX performance tracking which logs the rendering steps in the JavaFX pipeline and measures their performance on a frame by frame basis (I don't know how to do this).