首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >60 FPS的JavaFX FPS帽

60 FPS的JavaFX FPS帽
EN

Stack Overflow用户
提问于 2016-01-25 17:59:02
回答 1查看 2.5K关注 0票数 2

似乎有一个共同的理解,即JavaFX UI线程以每秒60次更新(12)为上限。据我理解,更新意味着pulse

脉冲是一种事件,它向JavaFX场景图指示是时候将场景图上的元素的状态与棱镜同步。脉冲以每秒60帧(fps)的速度节流,每当动画在场景图上运行时,就会触发脉冲。即使动画没有运行,当场景图中的某些内容被更改时,也会调度一个脉冲。例如,如果一个按钮的位置被改变,一个脉冲被调度。

来源:https://docs.oracle.com/javafx/2/architecture/jfxpub-architecture.htm

为了弄清楚如果对Platform.runLater的调用超过60个,那么会发生什么,我编写了这个小程序:

代码语言:javascript
复制
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class HighUITraffic extends Application {

    private int counter = 0;
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");

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

    @Override
    public void start(Stage primaryStage) {


        StackPane root = new StackPane();


        Timer timer = new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                long sheduleTime = System.currentTimeMillis();
                System.out.println("Task "+counter+" run at "+sdf.format(new Date(sheduleTime)));
                Platform.runLater(new RunInUI(counter));
                counter++;
            }
        };
        timer.schedule(task, 0, 10); // every 10ms => 100 per second

        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    private static class RunInUI implements Runnable {
        private final int counter;

        public RunInUI(int counter) {
            this.counter = counter;
        }

        @Override
        public void run() {
            long executionTime = System.currentTimeMillis();
            System.out.println("Task "+counter+" executed in Application Thread at "+sdf.format(new Date(executionTime)));
        }
    }
}

我的期望是:

  • Runnable被堆叠出来,UI线程分散运行,然后执行所有排队的Runables
  • UI线程执行前60次调用,然后对Runable进行进一步的排队。

但是,在一些初始延迟(Runable排队,然后所有处理都由UI线程处理)之后发生的情况是,两个线程按照顺序运行:

任务1281运行在2016-01-25T18:37:00.269在应用线程中执行的任务1281在2016-01-25T18:37:00.269任务1282运行在2016-01-25T18:37:00.274任务1282在应用线程中运行在2016-01-25T18:37:00.274任务1283运行在2016-01:37:00.279

在一秒钟内,任何一个线程都有超过60个调用(我尝试了100和200,没有任何区别)。

因此,我感到困惑:

  • 我是否误解了60脉冲上限的概念?
  • 这个小的执行片段没有那么重,这样可能会超出限制吗?

首先,我想知道的是,如果线程已经达到了其上限,那么在UI线程上推送的Runable会发生什么。在UI线程的一次运行中,所有排队排队的Runable都是按顺序执行的,还是只有一个Runable执行了,其余的必须等待?第二种情况会导致严重的问题,如果在UI线程上不断地推动更多的Runable而不是这个限制。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-01-25 18:44:37

脉冲的上限为60 are。提交给FX应用程序线程的Runnable不是。

据我所知,有两个线程: FX应用线程和棱镜渲染线程。从队列中消耗Runnable,并执行它们。棱镜渲染线程每秒执行多达60次,与FX应用线程同步(从而暂时阻止它执行新的运行程序)并呈现帧,然后释放FX应用线程。

因此,将尽可能快地执行您的Runnable,并且不限于每1/60次运行一次;但是它不会在帧呈现期间从队列中获得新的可运行状态。(用户事件在FX应用程序线程上以与您发布的Runnable类似的方式处理。)

所以有几种方法可以让坏事发生。一种是在上有代码(无论是在事件处理程序中还是在Runnable中发布到Platform.runLater()中),执行起来需要很长时间。这样做将阻止棱镜呈现线程与FX Application线程同步,这意味着在运行完成之前不能呈现下一个帧。另一种方法是用太多的Runnable轰炸,这样队列的增长速度超过了它们所能使用的速度。(从本质上说,由于队列总是有可用的东西,在这种情况下将成为一个繁忙的线程,因此无法保证棱镜呈现线程能够运行。)

因此,对您的问题的直接回答是,Runnables按照发布的顺序执行,就像在一个线程上一样迅速。以60 fps为上限的帧呈现将暂时停止从队列中消耗Runnable

在伪代码中,我猜测(这只是我的直觉,这不是基于源代码)看起来有点像

代码语言:javascript
复制
while (fxToolkitRunning()) {
    Runnable runnable = runnableQueue.take(); // block until something available
    acquireLock();
    runnable.run();
    releaseLock();
}

棱镜呈现线程看起来类似于:

代码语言:javascript
复制
while (fxToolkitRunning()) {
    while (! timeForNextFrame()) {
        sleep(timeUntilNextFrame());
    }
    acquireFXApplicationThreadLock();
    if (sceneNeedsUpdating()) {
        renderFrame();
    }
    releaseFXApplicationThreadLock();
}
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34999121

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档