【问题标题】:Java2D Performance IssuesJava2D 性能问题
【发布时间】:2008-10-13 06:57:08
【问题描述】:

我在使用 Java2D 时遇到了性能异常。我知道 sun.java2d.opengl VM 参数可以为 2D 启用 3D 加速,但即使使用它也有一些奇怪的问题。

这是我运行的测试结果:

在 JComponent 上使用 32x32 像素图块绘制 25x18 地图
图片 1 = .bmp 格式,图片 2 = .png 格式

没有-Dsun.java2d.opengl=true

120 FPS 使用 .BMP 图像 1
13 FPS 使用 .PNG 图像 2

使用 -Dsun.java2d.opengl=true

12 FPS 使用 .BMP 图像 1
700 FPS 使用 .PNG 图像 2

如果没有加速,我假设我在软件中执行的每个 drawImage() 都会发生某种转换,并且在 .PNG 的情况下会显着降低 FPS。但是,为什么随着加速,结果会发生变化(而 PNG 实际上执行得快得令人难以置信)?!疯了!

.BMP Image 1 被转换为 TYPE_INT_RGB 的图像类型。 .PNG Image 2 被转换为 TYPE_CUSTOM 的图像类型。为了在使用和不使用 opengl 加速的情况下获得一致的速度,我必须创建一个图像类型为 TYPE_INT_ARGB 的新 BufferedImage,并将图像 1 或图像 2 绘制到这个新图像上。

以下是运行结果:

没有-Dsun.java2d.opengl=true

120 FPS 使用 .BMP 图像 1
120 FPS 使用 .PNG 图像 2

使用 -Dsun.java2d.opengl=true

700 FPS 使用 .BMP 图像 1
700 FPS 使用 .PNG 图像 2

我真正的问题是,我可以假设 TYPE_INT_ARGB 将是所有系统和平台的本机图像类型吗?我假设这个值可能不同。有什么方法可以让我获得本机值,以便我始终可以创建新的 BufferedImages 以获得最佳性能?

提前谢谢...

【问题讨论】:

    标签: java java-2d


    【解决方案1】:

    我想我通过研究和整理来自太多 Google 搜索的点点滴滴找到了解决方案。

    在这里,cmets 和所有:

    private BufferedImage toCompatibleImage(BufferedImage image)
    {
        // obtain the current system graphical settings
        GraphicsConfiguration gfxConfig = GraphicsEnvironment.
            getLocalGraphicsEnvironment().getDefaultScreenDevice().
            getDefaultConfiguration();
    
        /*
         * if image is already compatible and optimized for current system 
         * settings, simply return it
         */
        if (image.getColorModel().equals(gfxConfig.getColorModel()))
            return image;
    
        // image is not optimized, so create a new image that is
        BufferedImage newImage = gfxConfig.createCompatibleImage(
                image.getWidth(), image.getHeight(), image.getTransparency());
    
        // get the graphics context of the new image to draw the old image on
        Graphics2D g2d = newImage.createGraphics();
    
        // actually draw the image and dispose of context no longer needed
        g2d.drawImage(image, 0, 0, null);
        g2d.dispose();
    
        // return the new optimized image
        return newImage; 
    }
    

    在我之前的帖子中,GraphicsConfiguration 包含在系统上创建优化图像所需的信息。它似乎工作得很好,但我原以为 Java 会自动为你做这件事。显然你不能对 Java 感到太舒服。 :) 我想我最终回答了我自己的问题。哦,好吧,希望它对我看到尝试将 Java 用于 2D 游戏的一些人有所帮助。

    【讨论】:

    • 这真是太棒了。它使我的代码运行得更快。非常感谢。
    • 我很抱歉迟迟没有将此标记为答案。
    • 非常感谢!
    【解决方案2】:

    嗯,这是一篇旧文章,但我想分享一下我对使用 Swing/AWT 直接绘制的发现,而无需使用 BufferedImage。

    当直接绘制到 int[] 缓冲区时,某些类型的绘图(如 3D)会更好。完成图像后,您可以使用 ImageProducer 实例(如 MemoryImageSource)来生成图像。我假设您知道如何直接执行绘图,而无需 Graphics/Graphics2 的帮助。

        /**
    * How to use MemoryImageSource to render images on JPanel
    * Example by A.Borges (2015)
    */
    public class MyCanvas extends JPanel implements Runnable {
    
    public int pixel[];
    public int width;
    public int height;
    private Image imageBuffer;   
    private MemoryImageSource mImageProducer;   
    private ColorModel cm;    
    private Thread thread;
    
    
    public MyCanvas() {
        super(true);
        thread = new Thread(this, "MyCanvas Thread");
    }
    
    /**
     * Call it after been visible and after resizes.
     */
    public void init(){        
        cm = getCompatibleColorModel();
        width = getWidth();
        height = getHeight();
        int screenSize = width * height;
        if(pixel == null || pixel.length < screenSize){
            pixel = new int[screenSize];
        }        
        mImageProducer =  new MemoryImageSource(width, height, cm, pixel,0, width);
        mImageProducer.setAnimated(true);
        mImageProducer.setFullBufferUpdates(true);  
        imageBuffer = Toolkit.getDefaultToolkit().createImage(mImageProducer);        
        if(thread.isInterrupted() || !thread.isAlive()){
            thread.start();
        }
    }
    /**
    * Do your draws in here !!
    * pixel is your canvas!
    */
    public /* abstract */ void render(){
        // rubisch draw
        int[] p = pixel; // this avoid crash when resizing
        if(p.length != width * height) return;        
        for(int x=0; x < width; x++){
            for(int y=0; y<height; y++){
                int color =  (((x + i) % 255) & 0xFF) << 16; //red
                    color |= (((y + j) % 255) & 0xFF) <<  8; //green
                    color |= (((y/2 + x/2 - j) % 255) & 0xFF) ;   //blue         
                p[ x + y * width] = color;
            }
        }        
        i += 1;
        j += 1;          
    }    
    private int i=1,j=256;
    
    @Override
    public void run() {
        while (true) {
            // request a JPanel re-drawing
            repaint();                                  
            try {Thread.sleep(5);} catch (InterruptedException e) {}
        }
    }
    
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        // perform draws on pixels
        render();
        // ask ImageProducer to update image
        mImageProducer.newPixels();            
        // draw it on panel          
        g.drawImage(this.imageBuffer, 0, 0, this);  
    }
    
    /**
     * Overrides ImageObserver.imageUpdate.
     * Always return true, assuming that imageBuffer is ready to go when called
     */
    @Override
    public boolean imageUpdate(Image image, int a, int b, int c, int d, int e) {
        return true;
    }
    }// end class
    

    请注意,我们需要 MemoryImageSourceImage 的唯一实例。不要为每个帧创建新的 Image 或新的 ImageProducer,除非您调整了 JPanel 的大小。请参阅上面的 init() 方法。

    在渲染线程中,请求 repaint()。在 Swing 上,repaint() 将调用重写的 paintComponent(),它会调用您的 render() 方法,然后要求您的 imageProducer 更新图片。 完成图像后,使用 Graphics.drawImage() 绘制它。

    要获得兼容的图像,请在创建 图像 时使用正确的 ColorModel。我使用 GraphicsConfiguration.getColorModel()

    /**
     * Get Best Color model available for current screen.
     * @return color model
     */
    protected static ColorModel getCompatibleColorModel(){        
        GraphicsConfiguration gfx_config = GraphicsEnvironment.
                getLocalGraphicsEnvironment().getDefaultScreenDevice().
                getDefaultConfiguration();        
        return gfx_config.getColorModel();
    }
    

    【讨论】:

    • 只是想知道什么样的人会否决正确的回复......这是(i)关于Java2D; (ii) 关于绘图图像的高性能; (iii) 关于渲染来自其他地方的图像(OpenGL、本机渲染器、软件渲染等)。那为什么要投反对票???
    【解决方案3】:

    我记得当我考虑用 Java 进行图形编程时,内置库很慢。我在 GameDev.Net 上被告知,任何做严肃事情的人都必须使用 jogl

    之类的东西

    【讨论】:

    • 好吧,因为最大的性能损失实际上是调用 drawImage(),所以我认为 700 FPS 的 450 次调用相当不错。我必须在 jogl 中编写相同的东西,看看是否有显着差异,但在最近的 Java 版本中,Java2D 取得了很大进展。
    猜你喜欢
    • 2012-05-14
    • 2013-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-13
    • 2022-01-04
    • 2011-02-27
    相关资源
    最近更新 更多