Java复制缓冲图像的性能问题

4

大家好!

我正在编写一款游戏。由于游戏板块的变化不多,我开始对其进行缓冲,并定期复制。然而,前景内容仍在不断变化,因此我需要高帧率。同时,我还想要进行缩放操作,这就是问题所在:为了节省内存,我重复使用缓冲区。每当我进行缩放操作时,应用程序会出现延迟,然后才能正常运行。

经过性能测试,我发现了两个性能杀手:

  • 清除背景缓冲区(4000x4000像素需要约29毫秒。为了保持透明度,我使用g.fillRect)

  • 将缓冲区复制回实际图像(当然不是实时的,但再次从getBufferStrategy()获取)。这需要300毫秒,下一次大约需要150毫秒,然后从第三帧开始就可以平稳运行。

为了阐明可能存在的问题,下面是一些代码。我按照以下方式创建自己的缓冲区:

GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice device = env.getDefaultScreenDevice();
GraphicsConfiguration config = device.getDefaultConfiguration();
image = config.createCompatibleImage(width, height,Transparency.TRANSLUCENT);

现在是将缓冲区复制回图像的部分。请注意,我需要剪切掉缓冲区的一些部分,这就是为什么我选择了最大参数调用。

g.drawImage(image, vs.boardOffsetX, vs.boardOffsetY, targetWidth, targetHeight, 0, 0, sourceWidth, sourceHeight, null);

最后,关于我的另一个问题:我按以下方式清除图像:

Graphics2D g = (Graphics2D) image.getGraphics();
Color transparent = new Color(0, 0, 0, 0);
g.setColor(transparent);
g.setComposite(AlphaComposite.Src);
g.fillRect(0, 0, image.getWidth(null), image.getHeight(null));

非常感谢!我已经被卡住了很长时间。如果您对我的方法有风格上的建议,请不要害羞,这是我第一次尝试图形处理。
谢谢!
编辑:我真正不理解的部分是,完全相同的操作需要非常不同的时间。除了我的线程之外,只有AWT-Thread在运行时,我会得到两个大约为300毫秒的时间,然后它就下降到像10微秒这样令人难以置信的速度!!!这对于复制1600万像素来说非常快速。有人理解这种效果吗?也许知道“预优化”这种行为的方法?

2
我看到的第一个问题是你有一个4000x4000像素的后备缓冲区。为什么需要这么大的缓冲区呢? - corsiKa
1
另外,您需要使用“image”的宽度和高度清除“g”实例吗?这将是一个4000x4000的矩形填充,还要透明。似乎不必要,只需清除屏幕上的任何矩形即可。此外,将alpha设置为0,您将获得完全不透明的颜色。也许可以放弃透明度。 - G_H
我需要这个缓冲区,因为在最大缩放时,需要显示的棋盘只有那么大。我同意,它肯定会慢,但在每帧或至少每次滚动时重新渲染完整内容会更慢。所以是的,我需要它。 只删除部分听起来不错,但比较复杂……稍后再研究一下。但还是谢谢! - julian
2个回答

2
很高兴告诉你,我并没有真正解决这个问题。相反,我取消了最后一次缩放步骤,将最大缓冲区限制为2000x2000,并消除了问题的最糟糕部分。现在缩放非常顺畅(我测量了第一个drawImage调用需要80毫秒。这仍然相当长,但由于视图变化迅速,你几乎不会注意到它)。
在我的研究中,我还发现我无法明确地强制Java创建所需大小的加速VolatileImages。那可能是问题所在 - 神只知道为什么。虽然我仍想找出原因...
但是,为了给有类似问题的人一个最终的建议:绕路走。
谢谢你的帮助!

1

不知道这会不会对你有帮助,但我的应用使用 System.arraycopy() 复制速度稍微快一些。

BufferedImage tmp = (BufferedImage) img;
int[] src = ((DataBufferInt) tmp.getRaster().getDataBuffer()).getData();
int[] dst = ((DataBufferInt) bi.getRaster().getDataBuffer()).getData();
System.arraycopy(src, 0, dst, 0, dst.length);

不错的想法。arraycopy通常在这方面具有最佳性能。但我不确定提问者是否需要复制他的图像。他需要将其绘制到图形上下文中。也许有一些方法可以复制到像素缓冲区? - G_H
这个想法很好。除了实际使用图形卡之外,无法更快了。但是:我正在尝试在策略缓冲区上绘制,这意味着我几乎只有Graphics2D对象。所以没有光栅... 有没有一种方法可以明确告诉Java我不想要任何调整大小?因为我的图像复制应该逐像素进行。 - julian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接