情况:
我创建了一个需要绘制简单矩形(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