JavaFX AnimationTimer以超过60 fps的速度运行

JavaFX AnimationTimer以超过60 fps的速度运行,java,linux,javafx,Java,Linux,Javafx,简短描述:运行一个简单的JavaFX程序 演示了AnimationTimer导致我的电脑几乎锁定 向上的该程序在其他电脑和我的电脑上运行良好 使用其他版本的Linux启动。详细说明 接着 在校对新版本的在线Java教科书时 我在运行书中的示例程序时遇到了一个问题 这说明了JavaFXAnimationTimer。程序源 是SimpleAnimationTimer.java。见下文。这是一个非常简单的问题 仅显示当前帧编号和 已用时间(以秒为单位)。预期的输出是 “每秒60帧”;即,显示的帧编号

简短描述:运行一个简单的JavaFX程序 演示了AnimationTimer导致我的电脑几乎锁定 向上的该程序在其他电脑和我的电脑上运行良好 使用其他版本的Linux启动。详细说明 接着

在校对新版本的在线Java教科书时 我在运行书中的示例程序时遇到了一个问题 这说明了JavaFXAnimationTimer。程序源 是SimpleAnimationTimer.java。见下文。这是一个非常简单的问题 仅显示当前帧编号和 已用时间(以秒为单位)。预期的输出是 “每秒60帧”;即,显示的帧编号 除以显示的经过时间应为60

在我的电脑上编译和运行时,它会淹没应用程序 窗口太大,我几乎无法移动鼠标 另一个窗口,以便我可以终止运行的程序。什么时候 我通知了这本书的作者,他回答说 在他所有的电脑(Ubuntu、LinuxMint、Mac、Windows)上运行良好。 我的电脑正在运行Fedora27

我从一个实时USB下载并运行了最新的Linux Mint, 然后在那里使用与使用的Java相同的Java测试程序 给27号软呢帽。程序在那里运行没有错误

接下来,我重新启动了一个旧的Fedora24并测试了程序 那里它运行正常

我从一个实时USB下载并运行Fedora28。这个 程序有问题

我的测试环境和结果的摘要

Java版本是1.8.0_181。这是来自甲骨文的

Linux Mint 19内核4.15.0-20-generic。没问题

Fedora24内核4.11.12。没问题

Fedora27内核4.17.17。有问题

Fedora28内核4.16.3。有问题

看起来Linux内核之间发生了一些变化 导致问题的版本4.15和4.16

修改了原始SimpleAnimationStarter.java源代码 在60帧后停止。Test02SimpleAnimationStarter.java 如下所示的源代码就是这样做的。运行此程序时 显示的帧数为60,与预期一致,显示的 运行时间为0.3秒,而不是预期的1秒

目前我不知该怎么办。这是吗 JavaFX问题?这是Linux内核的问题吗?某物 还有别的吗

如有任何建议/意见,将不胜感激。也, 运行这两个程序时看到结果会很好 在其他平台上——Windows、Mac和其他Linux版本

警告:首先运行Test02版本!确保时间已过 时间是一秒钟

消息来源如下。首先是SimpleAnimationStarter.java,然后是 Test02SimpleAnimationStarter.java

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.layout.BorderPane;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;

/**
 *  This file can be used to create very simple animations.  Just fill in
 *  the definition of drawFrame with the code to draw one frame of the 
 *  animation, and possibly change a few of the values in the rest of
 *  the program as noted below.
 */
public class SimpleAnimationStarter extends Application {

    /**
     * Draws one frame of an animation. This subroutine should be called
     * about 60 times per second.  It is responsible for redrawing the
     * entire drawing area. The parameter g is used for drawing. The frameNumber 
     * starts at zero and increases by 1 each time this subroutine is called.  
     * The parameter elapsedSeconds gives the number of seconds since the animation
     * was started.  By using frameNumber and/or elapsedSeconds in the drawing
     * code, you can make a picture that changes over time.  That's an animation.
     * The parameters width and height give the size of the drawing area, in pixels.  
     */
    public void drawFrame(GraphicsContext g, int frameNumber, double elapsedSeconds, int width, int height) {

        /* NOTE:  To get a different animation, just erase the contents of this 
         * subroutine and substitute your own. 
         */

        g.setFill(Color.WHITE);
        g.fillRect(0, 0, width, height); // First, fill the entire image with a background color!

        g.setFill(Color.BLACK);
        g.fillText( "Frame number " + frameNumber, 40, 50 );
        g.fillText( String.format("Elapsed Time: %1.1f seconds", elapsedSeconds), 40, 80);

    }

    //------ Implementation details: DO NOT EXPECT TO UNDERSTAND THIS ------


    private int frameNum;
    private long startTime;

    public void start(Stage stage) {
        int width = 800;   // The width of the image.  You can modify this value!
        int height = 600;  // The height of the image. You can modify this value!
        Canvas canvas = new Canvas(width,height);
        drawFrame(canvas.getGraphicsContext2D(), 0, 0, width, height);
        BorderPane root = new BorderPane(canvas);
        root.setStyle("-fx-border-width: 4px; -fx-border-color: #444");
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.setTitle("Simple Animation"); // STRING APPEARS IN WINDOW TITLEBAR!
        stage.show();
        stage.setResizable(false);
        AnimationTimer anim = new AnimationTimer() {
            public void handle(long now) {
                if (startTime < 0)
                    startTime = now;
                frameNum++;
                drawFrame(canvas.getGraphicsContext2D(), frameNum, (now-startTime)/1e9, width, height);
            }
        };
        startTime = -1;
        anim.start();
    } 

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

} // end SimpleAnimationStarter



import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.layout.BorderPane;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;

/**
 *  This file can be used to create very simple animations.  Just fill in
 *  the definition of drawFrame with the code to draw one frame of the 
 *  animation, and possibly change a few of the values in the rest of
 *  the program as noted below.
 */
public class Test02SimpleAnimationStarter extends Application {

    /**
     * Draws one frame of an animation. This subroutine should be called
     * about 60 times per second.  It is responsible for redrawing the
     * entire drawing area. The parameter g is used for drawing. The frameNumber 
     * starts at zero and increases by 1 each time this subroutine is called.  
     * The parameter elapsedSeconds gives the number of seconds since the animation
     * was started.  By using frameNumber and/or elapsedSeconds in the drawing
     * code, you can make a picture that changes over time.  That's an animation.
     * The parameters width and height give the size of the drawing area, in pixels.  
     */
    public void drawFrame(GraphicsContext g, int frameNumber, double elapsedSeconds, int width, int height) {

        /* NOTE:  To get a different animation, just erase the contents of this 
         * subroutine and substitute your own. 
         */

        g.setFill(Color.WHITE);
        g.fillRect(0, 0, width, height); // First, fill the entire image with a background color!

        g.setFill(Color.BLACK);
        g.fillText( "Frame number " + frameNumber, 40, 50 );
        g.fillText( String.format("Elapsed Time: %1.1f seconds", elapsedSeconds), 40, 80);

    }

    //------ Implementation details: DO NOT EXPECT TO UNDERSTAND THIS ------


    private int frameNum;
    private long startTime;

    AnimationTimer anim = null;  // Moved outside start() so handle() can call anim.stop()

    public void start(Stage stage) {
        int width = 800;   // The width of the image.  You can modify this value!
        int height = 600;  // The height of the image. You can modify this value!
        Canvas canvas = new Canvas(width,height);
        drawFrame(canvas.getGraphicsContext2D(), 0, 0, width, height);
        BorderPane root = new BorderPane(canvas);
        root.setStyle("-fx-border-width: 4px; -fx-border-color: #444");
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.setTitle("Simple Animation"); // STRING APPEARS IN WINDOW TITLEBAR!
        stage.show();
        stage.setResizable(false);
//        AnimationTimer anim = new AnimationTimer() {            
        anim = new AnimationTimer() {
            // Constants: change NUM_SECS and/or framesPerSec
            private final int NUM_SECS = 1; // Desired number of seconds.
            private final int framesPerSec = 60; // Expected frames per second.
            private final int NUM_FRAMES = NUM_SECS*framesPerSec;

            public void handle(long now) {
                if (startTime < 0)
                    startTime = now;
                frameNum++;
                if (frameNum < NUM_FRAMES+1)
                {
                    drawFrame(canvas.getGraphicsContext2D(),
                              frameNum,
                              (now-startTime)/1e9,
                              width, height);
                }
                else if (frameNum == NUM_FRAMES+1)
                {
                    anim.stop();
                    System.out.println("Animation stopped");
                }
                else if (frameNum == NUM_FRAMES+2)
                {
                    System.out.println("Animation kept going after stop ???");
                }
            }
        };
        startTime = -1;
        anim.start();
    }

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

} // end Test02SimpleAnimationStarter
导入javafx.animation.AnimationTimer;
导入javafx.application.application;
导入javafx.scene.layout.BorderPane;
导入javafx.scene.scene;
导入javafx.stage.stage;
导入javafx.scene.canvas.canvas;
导入javafx.scene.canvas.GraphicsContext;
导入javafx.scene.paint.Color;
/**
*此文件可用于创建非常简单的动画。只需填写
*drawFrame的定义,代码用于绘制
*动画,并可能更改其余部分中的一些值
*该计划如下所述。
*/
公共类SimpleAnimationStarter扩展了应用程序{
/**
*绘制动画的一帧。应调用此子例程
*大约每秒60次。它负责重新绘制
*整个绘图区域。参数g用于绘图。帧编号
*从零开始,每次调用此子例程时增加1。
*参数elapsedSeconds给出自动画开始的秒数
*已启动。通过在图形中使用frameNumber和/或elapsedSeconds
*代码,你可以制作一个随时间变化的图片。这是一个动画。
*“宽度”和“高度”参数给出绘图区域的大小(以像素为单位)。
*/
公共无效绘图框(GraphicsContext g、int frameNumber、双elapsedSeconds、int width、int height){
/*注意:要获得不同的动画,只需删除此动画的内容
*子例程并替换您自己的。
*/
g、 设置填充(颜色:白色);
g、 fillRect(0,0,宽度,高度);//首先,用背景色填充整个图像!
g、 设置填充(颜色:黑色);
g、 填充文本(“帧编号”+帧编号,40,50);
g、 fillText(String.format(“运行时间:%1.1f秒”,elapsedSeconds),40,80);
}
//------实施细节:不要期望理解这一点------
私有int-frameNum;
私人长启动时间;
公众假期开始(阶段){
int width=800;//图像的宽度。您可以修改此值!
int height=600;//图像的高度。您可以修改此值!
画布=新画布(宽度、高度);
drawFrame(canvas.getGraphicsContext2D(),0,0,宽度,高度);
BorderPane根=新的BorderPane(画布);
root.setStyle(“-fx边框宽度:4px;-fx边框颜色:#444”);
场景=新场景(根);
舞台场景;
stage.setTitle(“简单动画”);//字符串出现在窗口标题栏中!
stage.show();
阶段。可设置大小(假);
AnimationTimer anim=新的AnimationTimer(){
公共无效句柄(长){
如果(开始时间<0)
开始时间=现在;
frameNum++;
drawFrame(canvas.getGraphicsContext2D(),frameNum,(现在开始时间)/1e9,宽度,高度);
}
};
开始时间=-1;
anim.start();
} 
公共静态void main(字符串[]args){
发射();
}
}//结束SimpleImationStarter
导入javafx.animation.AnimationTimer;
导入javafx.application.application;
导入javafx.scene.layout.BorderPane;
导入javafx.scene.scene;
导入javafx.stage.stage;
英普