【问题标题】:How does the optimization of Canvas drawImage work?Canvas drawImage 的优化是如何工作的?
【发布时间】:2015-07-28 07:40:39
【问题描述】:

问题

我尝试使用 Canvas 创建粒子系统。有趣的是,drawImage 方法非常快。即使使用 drawImage,我也以 60 fps 的速度获得了 90.000 个粒子。但是,那只是相同的图像。当我对粒子使用不同的图像时,性能会大大下降。然后我改变了图像的顺序,所以 e。 G。首先绘制image1的所有粒子,然后绘制image2的所有粒子,等等,性能又好了。

问题

有谁知道这是为什么? drawImage 中是否有一些必须考虑的内部缓存机制?

代码

下面是示例代码:

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);
    }

}

这是非常基本的。从一个矩形创建一个图像,然后放入一个数组中,然后在 AnimationTimer 循环中,图像在 Canvas 上绘制 numIterations-times。

当您使用index = i % 1 时,即。 e.一遍又一遍相同的图像,然后我的系统上的 fps 为 60 fps。如果你使用index = i % 2,我。 e.交替图像,然后我的系统上的 fps 为 14 fps。这是一个相当大的差异。

非常感谢您的帮助!

【问题讨论】:

    标签: canvas javafx


    【解决方案1】:

    画布工作原理的背景

    Canvas 保留一个缓冲区。每次您向画布发出命令(例如绘制图像)时,该命令都会附加到缓冲区中。在下一个渲染脉冲上,通过处理每个命令并将它们渲染到纹理来刷新缓冲区。纹理被发送到将在屏幕上渲染的显卡。

    在源代码中跟踪画布操作

    注意:此处引用的链接代码已过时且不是官方的,因为它是一个反向移植,但它是最容易在线交叉引用的代码,并且实现与 JDK 中的官方代码相似(或相同)。所以只需追踪它:

    您调用 drawImage 和 GraphicsContext writes an imagethe buffer

    对于 JavaFX 图形系统的下一个脉冲或快照请求,缓冲区被清空,issuing render commandsrender command for the image 使用cached texture from a resource factory 来渲染图像。

    为什么你的应用会变慢(我实际上不知道)

    我想也许,当您交替图像时,您并没有保留要在场景中渲染的“实时”图像作为场景图的一部分,因此纹理缓存会驱逐旧图像,从而导致系统抖动和为图像重新创建纹理 (just a guess)。但是,我在调试器中逐步完成了该过程(在您的应用程序运行几秒钟后,删除屏幕上的 fps 标签并在 BaseShaderGraphics.drawTexture 中设置断点)。您将看到相同的缓存纹理被重复使用。纹理缓存似乎表现良好,并且正在为每个图像缓存纹理,所以我真的不知道你观察到的减速的根本原因是什么。

    【讨论】:

      猜你喜欢
      • 2011-07-20
      • 1970-01-01
      • 2022-09-30
      • 2020-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-20
      相关资源
      最近更新 更多