使用JavaFX AnimationTimer停止屏幕撕裂

使用JavaFX AnimationTimer停止屏幕撕裂,java,animation,javafx,Java,Animation,Javafx,在JavaFX中,我觉得使用AnimationTimer频繁更新Canvas节点是合适的;也就是说,用于光栅化动画而不是矢量动画。我遇到了一个问题,可能与VSync有关。我的场景中有一个单一的画布,其中包含频繁变化的2D数据(程序的其余部分是通过节点完成的)。当我这样做的时候,我倾向于在屏幕中间看到一个撕裂,这意味着AnimationTimer可能与监视器刷新率不同步 此(简化)代码详细显示了错误。请注意,我不知道它是否依赖于显示硬件,撕裂的确切位置可能会略有改变 import javafx.a

在JavaFX中,我觉得使用AnimationTimer频繁更新Canvas节点是合适的;也就是说,用于光栅化动画而不是矢量动画。我遇到了一个问题,可能与VSync有关。我的场景中有一个单一的画布,其中包含频繁变化的2D数据(程序的其余部分是通过节点完成的)。当我这样做的时候,我倾向于在屏幕中间看到一个撕裂,这意味着AnimationTimer可能与监视器刷新率不同步

此(简化)代码详细显示了错误。请注意,我不知道它是否依赖于显示硬件,撕裂的确切位置可能会略有改变

import javafx.application.*;
import javafx.animation.*;
import javafx.scene.*;
import javafx.scene.canvas.*;
import javafx.stage.*;
import javafx.event.*;
import javafx.scene.input.*;
import javafx.scene.paint.*;

public class BugDemo extends Application {

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

    private Canvas background = new Canvas();
    private Color bgColor = Color.rgb(0, 0, 0);

    @Override
    public void start(Stage primaryStage) throws Exception {
        Group root = new Group();

        startLoops(root);
        Scene scene = new Scene(root);

        initUserControls(scene);
        background.widthProperty().bind(scene.widthProperty());
        background.heightProperty().bind(scene.heightProperty());
        root.getChildren().add(background);

        primaryStage.setScene(scene);

        primaryStage.setTitle("Bug Demo");

        primaryStage.setMaximized(true);
        primaryStage.show();
    }

    /**
     * Initializes all running control managers for game field
     * @param scene primary scene of game
     */
    protected void initUserControls(Scene scene) {
        EventHandler<MouseEvent> handler = new EventHandler<MouseEvent>(){

            @Override
            public void handle(MouseEvent event) {
                bgColor = Color.rgb((int)((event.getX() / background.getWidth()) * 255),
                        (int)((event.getY() / background.getHeight()) * 255),
                        255);
            }};
        scene.getRoot().setOnMouseMoved(handler);
        scene.getRoot().setOnMouseDragged(handler);
    }

    /**
     * Starts all game loops, which will at the beginning of the simulation and often for the extent
     * of it.
     * @param parent Root node
     */
    protected void startLoops(Parent parent) {
        GraphicsContext gc = background.getGraphicsContext2D();

        new AnimationTimer() {

            @Override
            public void handle(long now) {
                drawBackground(gc);
            }}.start();
    }

    protected void drawBackground(GraphicsContext gc) {
        gc.setFill(bgColor);
        gc.fillRect(0, 0, background.getWidth(), background.getHeight());
    }

}
导入javafx.application.*;
导入javafx.animation.*;
导入javafx.scene.*;
导入javafx.scene.canvas.*;
导入javafx.stage.*;
导入javafx.event.*;
导入javafx.scene.input.*;
导入javafx.scene.paint.*;
公共类BugDemo扩展了应用程序{
公共静态void main(字符串…参数){
发射(args);
}
私有画布背景=新画布();
私有颜色bgColor=Color.rgb(0,0,0);
@凌驾
public void start(Stage primaryStage)引发异常{
组根=新组();
startoops(根);
场景=新场景(根);
初始化用户控件(场景);
background.widthProperty().bind(scene.widthProperty());
background.heightProperty().bind(scene.heightProperty());
root.getChildren().add(后台);
初级阶段。场景(场景);
setTitle(“Bug演示”);
primaryStage.setMaximized(真);
primaryStage.show();
}
/**
*初始化游戏场的所有运行控制管理器
*@param scene游戏主场景
*/
受保护的void initUserControls(场景){
EventHandler=新的EventHandler(){
@凌驾
公共无效句柄(MouseeEvent事件){
bgColor=Color.rgb((int)((event.getX()/background.getWidth())*255),
(int)((event.getY()/background.getHeight())*255),
255);
}};
scene.getRoot().setOnMouseMoved(处理程序);
scene.getRoot();
}
/**
*启动所有游戏循环,这将在模拟开始时启动,通常用于范围
*当然。
*@param父根节点
*/
受保护的无效对象(父对象){
GraphicsContext gc=background.getGraphicsContext2D();
新的AnimationTimer(){
@凌驾
公共无效句柄(长){
退税地(gc);
}}.start();
}
受保护的无效牵引地面(GraphicsContext gc){
gc.setFill(bgColor);
gc.fillRect(0,0,background.getWidth(),background.getHeight());
}
}
在场景上移动鼠标光标将导致画布背景色发生移动。当你移动鼠标时,你会注意到(大概)画面上有撕裂

很可能我只是JavaFX的一个N00B,我应该在某处调用一个非常友好的.sync()-ish方法;但我还没找到。我发现提交给Oracle的bug与此类似。我的答案可能只是回避使用Canvas和AnimationTimer制作动画。尽管如此,即使目前还没有解决方案,如果有人有解决办法,我也很乐意听到

谢谢

发现了问题(感谢@VGR指出系统行为一致性的不足)!看起来它根本不是Java;是马可。我把桌面窗口管理器换成了Compiz;现在眼泪也不见了


显然,这在Java中根本不是一个bug;这是使用马可的一个折衷方案。这是一种解脱

我看不到任何撕裂,甚至当我运行
-Dprism.order=sw
,这让我怀疑这是视频驱动程序的问题。你在不同的机器上试过吗?我也曾想过在这台机器上的不同操作系统下运行它;只是为了确定问题更可能出现在软件端还是硬件端。我将把它打包成一个jar文件,并在契约完成后报告。