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

情况:

我创建了一个需要绘制简单矩形(1×1 – 3×3大小)的应用程序取决于以20,40或甚至60 FPS速率存储在相应大小(900×900 – 300×300大小)的数组中的一些变量.

这是我的绘图方法:

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执行此操作非常慢!在5-10秒内绘制10次迭代(应该在20FPS速率下以0.5秒绘制),这在这种情况下显然是不可接受的.

题 :

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

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

解决方法:

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

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

pixelwriter使用缓冲区

//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
上一篇:C# 生成海报,文本区域指定和换行,图片合成


下一篇:PostgreSQL配置文件--资源使用(除WAL外)