如何提高JavaFX图形绘制的性能?

时间:2016-06-06 00:58:10

标签: java performance javafx drawing

情况:

我创建的应用程序需要绘制简单的矩形(1x1 - 3x3大小)取决于存储在相应大小的数组(900x900 - 300x300大小)中的一些变量,以20,40或甚至60 FPS的速率。 / p>

这是我的绘图方法:

protected void drawCurrentState()
    {   
        for (int i=0; i<gameLogic.size; i++)
        {
            for (int j=0; j<gameLogic.size; j++)
            {
                if (gameLogic.previousStepTable == null || gameLogic.previousStepTable[i][j] != gameLogic.gameTable[i][j])
                {
                    if (gameLogic.gameTable[i][j].state)
                        graphicsContext.setFill(gameLogic.gameTable[i][j].color); 
                    else
                        graphicsContext.setFill(Color.WHITE); 

                    graphicsContext.fillRect(leftMargin + (j * (cellSize + cellMargin)),
                        topMargin + (i * (cellSize + cellMargin)), cellSize, cellSize);
                }

            }
        }
    }

正如您可能已经注意到的那样,只有在需要时才会执行绘图(当状态从上一步开始变化时)。所有其他操作(gameLogic中的计算变量状态等)几乎没有时间,也没有影响性能。

问题:

JavaFX执行此操作非常慢! 10次​​迭代(应在20FPS速率下以0.5秒绘制)在5-10秒内绘制,这在这种情况下显然是不可接受的。

问题:

有没有办法大规模地将绘图操作加速到预期的性能水平(例如,一秒钟内迭代40或60次)?

在这种情况下,绘画表现如此糟糕的原因是什么?

2 个答案:

答案 0 :(得分:6)

我在几个不同的实现上运行了一些基准测试,如下所示:

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.canvas.*;
import javafx.scene.image.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import java.nio.IntBuffer;

public class CanvasUpdater extends Application {
    private static final int CELL_SIZE = 2;
    private static final int BOARD_SIZE = 900;

    private static final int W = BOARD_SIZE * CELL_SIZE;
    private static final int H = BOARD_SIZE * CELL_SIZE;

    private static final double CYCLE_TIME_IN_MS = 5_000;

    private final WritablePixelFormat<IntBuffer> pixelFormat = 
            PixelFormat.getIntArgbPreInstance();

    private Canvas canvas = new Canvas(W, H);
    private GraphicsContext gc = canvas.getGraphicsContext2D();
    private int[] buffer = new int[W * H];

    @Override
    public void start(Stage stage) {
        final long start = System.nanoTime();
        AnimationTimer timer = new AnimationTimer() {
            @Override
            public void handle(long now) {
                double t = 
                        ((now - start) % (1_000_000 * CYCLE_TIME_IN_MS * 2)) / 
                        (1_000_000.0 * CYCLE_TIME_IN_MS);
                if (t > 1) t = 1 - (t - 1);
                Color c1 = Color.RED.interpolate(Color.BLUE, t);
                Color c2 = Color.BLUE.interpolate(Color.RED, t);

                gc.clearRect(0, 0, W, H);

//                Draw using fillRect
//
//                for (int i = 0; i < W; i += CELL_SIZE) {
//                    for (int j = 0; j < H; j += CELL_SIZE) {
//                        gc.setFill(
//                                (i/CELL_SIZE + j/CELL_SIZE) % 2 == 0
//                                        ? c1
//                                        : c2
//                        );
//                        gc.fillRect(i, j, CELL_SIZE, CELL_SIZE);
//                    }
//                }

//                Draw using setColor
//
//                PixelWriter p = gc.getPixelWriter();
//                for (int i = 0; i < W; i += CELL_SIZE) {
//                    for (int j = 0; j < H; j += CELL_SIZE) {
//                        Color c =
//                                (i/CELL_SIZE + j/CELL_SIZE) % 2 == 0
//                                        ? c1
//                                        : c2;
//                        for (int dx = 0; dx < CELL_SIZE; dx++) {
//                            for (int dy = 0 ; dy < CELL_SIZE; dy++) {
//                                p.setColor(i + dx, j + dy, c);
//                            }
//                        }
//                    }
//                }

//              Draw using buffer
//
                int ci1 = toInt(c1);
                int ci2 = toInt(c2);

                for (int i = 0; i < W; i += CELL_SIZE) {
                    for (int j = 0; j < H; j += CELL_SIZE) {
                        int ci =
                                (i/CELL_SIZE + j/CELL_SIZE) % 2 == 0
                                        ? ci1
                                        : ci2;
                        for (int dx = 0; dx < CELL_SIZE; dx++) {
                            for (int dy = 0 ; dy < CELL_SIZE; dy++) {
                                buffer[i + dx + W * (j + dy)] = ci;
                            }
                        }
                    }
                }

                PixelWriter p = gc.getPixelWriter();
                p.setPixels(0, 0, W, H, pixelFormat, buffer, 0, W);
            }
        };
        timer.start();

        stage.setScene(new Scene(new Group(canvas)));
        stage.show();
    }

    private int toInt(Color c) {
        return
                (                      255  << 24) |
                ((int) (c.getRed()   * 255) << 16) |
                ((int) (c.getGreen() * 255) << 8)  |
                ((int) (c.getBlue()  * 255));
    }

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

上面的程序是在各种实现的情况下运行的,其中JavaFX PulseLogger已打开-Djavafx.pulseLogger=true。如您所见,使用缓冲区设置PixelWriter中的像素比填充画布中的矩形快50倍,比调用PixelWriter上的setColor快100倍。

fillrect

//PULSE: 81 [217ms:424ms]
//T15 (58 +0ms): CSS Pass
//T15 (58 +0ms): Layout Pass
//T15 (58 +0ms): Update bounds
//T15 (58 +155ms): Waiting for previous rendering
//T15 (214 +0ms): Copy state to render graph
//T12 (214 +0ms): Dirty Opts Computed
//T12 (214 +209ms): Painting
//T12 (424 +0ms): Presenting
//Counters:
//Nodes rendered: 2
//Nodes visited during render: 2
使用setColor

pixelwriter

//PULSE: 33 [370ms:716ms]
//T15 (123 +0ms): CSS Pass
//T15 (123 +0ms): Layout Pass
//T15 (123 +0ms): Update bounds
//T15 (123 +244ms): Waiting for previous rendering
//T15 (368 +0ms): Copy state to render graph
//T12 (368 +0ms): Dirty Opts Computed
//T12 (368 +347ms): Painting
//T12 (715 +0ms): Presenting
//Counters:
//Nodes rendered: 2
//Nodes visited during render: 2

使用缓冲区的

像素写入器
//PULSE: 270 [33ms:37ms]
//T15 (28 +0ms): CSS Pass
//T15 (28 +0ms): Layout Pass
//T15 (28 +0ms): Update bounds
//T15 (28 +0ms): Waiting for previous rendering
//T15 (28 +0ms): Copy state to render graph
//T12 (29 +0ms): Dirty Opts Computed
//T12 (29 +7ms): Painting
//T12 (36 +0ms): Presenting
//Counters:
//Nodes rendered: 2
//Nodes visited during render: 2

答案 1 :(得分:1)

这看起来像一个标准问题:以像素方式绘制是非常慢的。绘制你的小矩形符合几乎像素的条件。

如果您直接在某些画布上绘图,请尝试git push,这是一个异常内存数据结构,应该比访问图形卡内存快得多。然后将图像绘制到它所属的位置。

否则,请手动设置像素并使用BufferedImage或类似内容来绘制int[] rgbArray