我创建的应用程序需要绘制简单的矩形(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次)?
在这种情况下,绘画表现如此糟糕的原因是什么?
答案 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
。