Canvas 画布drawImage的优化是如何工作的?
问题 我尝试使用画布创建粒子系统。有趣的是,这种方法绘制图像的速度非常快。即使使用drawImage,我也能以60 fps的速度获得90.000个粒子。然而,这只是同一个图像。当我对粒子使用不同的图像时,性能显著下降。然后我改变了图像的顺序,使e。G首先绘制image1的所有粒子,然后绘制image2的所有粒子,以此类推,性能再次良好 问题 有人知道为什么吗?DRAWMAGE中有一些内部缓存机制需要考虑吗? 代码 下面是示例代码:Canvas 画布drawImage的优化是如何工作的?,canvas,javafx,Canvas,Javafx,问题 我尝试使用画布创建粒子系统。有趣的是,这种方法绘制图像的速度非常快。即使使用drawImage,我也能以60 fps的速度获得90.000个粒子。然而,这只是同一个图像。当我对粒子使用不同的图像时,性能显著下降。然后我改变了图像的顺序,使e。G首先绘制image1的所有粒子,然后绘制image2的所有粒子,以此类推,性能再次良好 问题 有人知道为什么吗?DRAWMAGE中有一些内部缓存机制需要考虑吗? 代码 下面是示例代码: import javafx.animation.Animatio
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class CanvasExample extends Application {
GraphicsContext gc;
double width = 800;
double height = 600;
Image[] images;
@Override
public void start(Stage primaryStage) {
Canvas canvas = new Canvas( width, height);
gc = canvas.getGraphicsContext2D();
BorderPane root = new BorderPane();
root.setCenter( canvas);
Scene scene = new Scene( root, width, height);
scene.setFill(Color.BEIGE);
primaryStage.setScene(scene);
primaryStage.show();
createImages(255);
AnimationTimer loop = new AnimationTimer() {
double prev = 0;
double frameCount = 0;
double fps = 0;
@Override
public void handle(long now) {
// very basic frame counter
if( now - prev > 1_000_000_000) {
System.out.println( "FPS: " + frameCount);
fps = frameCount;
prev = now;
frameCount = 0;
} else {
frameCount++;
}
// clear canvas
gc.setFill( Color.BLACK);
gc.fillRect(0, 0, width, height);
// paint images
int numIterations = 90000;
for( int i=0; i < numIterations; i++) {
int index = i % 2; // <==== change here: i % 1 is fast, i % 2 is slow
gc.drawImage(images[ index], 100, 100);
}
gc.setFill(Color.WHITE);
gc.fillText("fps: " + fps, 0, 10);
}
};
loop.start();
}
public void createImages( int count) {
Rectangle rect = new Rectangle(10,10);
rect.setFill(Color.RED);
images = new Image[count];
for( int i=0; i < count; i++) {
images[i] = createImage(rect);
}
}
/**
* Snapshot an image out of a node, consider transparency.
*
* @param node
* @return
*/
public Image createImage(Node node) {
WritableImage wi;
SnapshotParameters parameters = new SnapshotParameters();
parameters.setFill(Color.TRANSPARENT);
int imageWidth = (int) node.getBoundsInLocal().getWidth();
int imageHeight = (int) node.getBoundsInLocal().getHeight();
wi = new WritableImage(imageWidth, imageHeight);
node.snapshot(parameters, wi);
return wi;
}
public static void main(String[] args) {
launch(args);
}
}
导入javafx.animation.AnimationTimer;
导入javafx.application.application;
导入javafx.scene.Node;
导入javafx.scene.scene;
导入javafx.scene.Snapshot参数;
导入javafx.scene.canvas.canvas;
导入javafx.scene.canvas.GraphicsContext;
导入javafx.scene.image.image;
导入javafx.scene.image.WritableImage;
导入javafx.scene.layout.BorderPane;
导入javafx.scene.paint.Color;
导入javafx.scene.shape.Rectangle;
导入javafx.stage.stage;
公共类CanvasExample扩展了应用程序{
GraphicsContext gc;
双倍宽度=800;
双倍高度=600;
图像[]图像;
@凌驾
公共无效开始(阶段primaryStage){
画布=新画布(宽度、高度);
gc=canvas.getGraphicsContext2D();
BorderPane根=新的BorderPane();
root.setCenter(画布);
场景=新场景(根、宽度、高度);
场景。背景填充(颜色:米色);
初级阶段。场景(场景);
primaryStage.show();
创建图像(255);
AnimationTimer循环=新建AnimationTimer(){
双上一个=0;
双帧计数=0;
双fps=0;
@凌驾
公共无效句柄(长){
//非常基本的帧计数器
如果(现在-上一个>1_000_000_000){
System.out.println(“FPS:+frameCount”);
fps=帧数;
上一个=现在;
帧数=0;
}否则{
frameCount++;
}
//透明帆布
gc.setFill(颜色为黑色);
gc.fillRect(0,0,宽度,高度);
//绘画图像
整数=90000;
对于(int i=0;i
画布保留一个缓冲区。每次向画布发出命令(如绘制图像)时,该命令都会附加到缓冲区。在下一个渲染脉冲中,通过处理每个命令并将其渲染为纹理来刷新缓冲区。纹理被发送到图形卡,图形卡将在屏幕上渲染它
在源代码中跟踪画布操作
注意:此处引用的链接代码已经过时,不是官方代码,因为它是一个后端口,但它是最容易在线交叉引用的代码,并且实现与JDK中的官方代码类似(或相同)。因此只需跟踪它:
您可以调用drawImage和GraphicsContext来创建
对于JavaFX图形系统的下一个脉冲或快照请求,缓冲区将清空。使用渲染图像
为什么你的应用速度变慢(我不知道)
我想,当您替换图像时,您可能没有保留要在场景中渲染的“活动”图像作为场景图的一部分,因此纹理缓存会逐出旧图像,导致系统抖动并重新创建图像的纹理()。但是,我在调试器中完成了该过程(在应用程序运行几秒钟后,删除屏幕上的fps标签并在BaseShaderGraphics.drawTexture中设置断点)。您将看到相同的缓存纹理被重用。纹理缓存似乎表现良好,并且正在为每个图像缓存纹理,因此我真的不知道您观察到的减速的根本原因是什么