奇怪的BufferStrategy问题 – 游戏仅在英特尔GPU上运行速度很快

我遇到了一个非常奇怪的问题,我试着寻找几天和几天的答案。 我的游戏刚刚获得了一个新的粒子系统,但速度太慢而无法播放。 不幸的是,BufferedImage转换非常慢。 爆炸效果包括从.png文件加载的大约200个白色精灵,随机旋转,缩放和着色,以随机速度移动。

我试图通过三重/双重缓冲使性能更好,并遇到了一些问题。

我的第一次尝试是使用JPanel进行游戏。 我在JFrame的类(Main)中设置缓冲区,然后在Game(扩展JPanel)类中完成绘图,但没有Graphics g = bufferstrategy.getDrawGraphics();. 然后,在绘图方法结束时,我显示缓冲区IF没有丢失。 缓冲区总是“丢失”,因为我没有使用它的Graphics对象进行绘图。 但! 游戏运行得和地狱一样快! 在实际使用中没有缓冲! 但是怎么样?

此尝试最终没有出现图形错误,并且具有巨大的性能提升 – 但仅限于nVidia / AMD显卡。 英特尔GPU无法处理这个问题,屏幕闪烁白光。

所以,我最终设置并正确使用BufferStrategy。 Game类现在扩展Canvas,而不是JPanel,因为从JFrame获取Graphics,并且使用它在JPanel上绘制最终会在偏移量中,因为它在标题栏下绘制。 仍然很快,修复60 FPS。

现在,当我在JFrame(Main类)中创建BufferStrategy时,根本没有图片。 我通过在Game类(Canvas)中设置BufferStrategy来纠正这个问题。 图片现在是正确的,但游戏本身就像蜗牛一样慢。 一次爆炸使FPS下降至~10,但仅限于nVidia / AMD。 具有讽刺意味的。 即使是老式的英特尔GPU也能以60 FPS处理它,我还是在一台具有5到6年历史的集成英特尔GPU上以60 FPS的速度运行了10000个粒子。 发生爆炸时,我的卡会碰到高达100%的负载。

这是我的主要代码(整个代码不清楚且很长):

public class Game extends Canvas { -snip- public void tick() { BufferStrategy bf = getBufferStrategy(); Graphics g = null; try { g = bf.getDrawGraphics(); paint(g); } finally { g.dispose(); } if (!bf.contentsLost()) { bf.show(); } else { System.err.println("Buffer lost!"); } Toolkit.getDefaultToolkit().sync(); } public void setBuffers() { GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice gs = ge.getDefaultScreenDevice(); GraphicsConfiguration gc = gs.getDefaultConfiguration(); if (gc.getBufferCapabilities().isMultiBufferAvailable()) { createBufferStrategy(3); System.out.println("Triple buffering active"); } else { createBufferStrategy(2); System.err.println("Triple buffering not supported by the GPU"); System.out.println("Double buffering active"); } System.out.println("FullScreen required: " + getBufferStrategy().getCapabilities().isFullScreenRequired()); System.out.println("Page flipping: " + getBufferStrategy().getCapabilities().isPageFlipping()); } public void paint(Graphics g) { super.paint(g); //set up RenderingHints, draw stuff } -snip snip- } 

当然,我会在游戏启动后立即调用setBuffers()。

我希望你能帮到我,这个问题非常重要,因为在我看来使用VolatileImage不会带来性能提升,因为需要使用BufferedImage进行图像处理。 我敢打赌,我错过了一些微不足道的事情,或者做错了事。

以下是我的电脑规格,只是为了表明它不是硬件问题:Intel Core i7-3770k @ 4.3GHz,nVidia GTX 460,12 GB ram

“快速”计算机:Intel Core 2 Duo @ 2.7 GHz,集成Intel Graphics,2 GB RAM

谢谢你的帮助和时间! 🙂

编辑可以VolatileImage帮助吗? 如果我知道的话,必须使用BufferedImages完成图像处理,但绘制它们是缓慢的。

这里有几件事需要检查:


在不知道setBuffers函数的控制台/错误输出的情况下,很难说清楚。 英特尔是否使用createBufferStrategy(2); 而NV使用createBufferStrategy(3); ? 如果是这样可能是使用额外内存的问题的一部分。


你试过java2d System.properties了吗? http://docs.oracle.com/javase/1.5.0/docs/guide/2d/flags.html特别是用于调试的trace属性。

仅限Windows

 System.setProperty("sun.java2d.transaccel", "True"); System.setProperty("sun.java2d.d3d", "True"); System.setProperty("sun.java2d.ddforcevram", "True"); 

所有平台

 System.setProperty("sun.java2d.opengl", "True"); 

用于调试

 System.setProperty("sun.java2d.trace", "timestamp,log,count"); //// -Dsun.java2d.trace=[log[,timestamp]],[count],[out:],[help],[verbose] 

Toolkit.getDefaultToolkit()同步(); 实际上并不强制监视VSync,这是由BufferCapabilities(在你的setBuffers函数中)完成的。

  BufferStrategy bf = getBufferStrategy(); if (bf != null) { BufferCapabilities caps = bf.getCapabilities(); try { Class ebcClass = Class.forName( "sun.java2d.pipe.hw.ExtendedBufferCapabilities"); Class vstClass = Class.forName( "sun.java2d.pipe.hw.ExtendedBufferCapabilities$VSyncType"); Constructor ebcConstructor = ebcClass.getConstructor( new Class[] { BufferCapabilities.class, vstClass }); Object vSyncType = vstClass.getField("VSYNC_ON").get(null); BufferCapabilities newCaps = (BufferCapabilities)ebcConstructor.newInstance( new Object[] { caps, vSyncType }); createBufferStrategy(2, newCaps); // TODO: if success, setCanChangeRefreshRate(false) and setRefreshRate(60). // Possibly override refreshRateSync()? } catch (Throwable t) { // Ignore t.printStackTrace(); } } 

编辑此代码来自( http://pulpcore.googlecode.com/hg-history/3c4001969922b93048e0a166395115df059a2059/src/pulpcore/platform/applet/BufferStrategySurface.java

此外,您应该在Canvas构造函数运行后调用setBuffers,并将其添加到JFrame中。


我“相对确定”你不需要打电话给super.paint(g); 我不知道它是什么导致了你的具体问题,但是你应该从你的绘画function中删除这一行。


虽然您没有显示代码,但使用200个随机移动的精灵的爆炸机制可能是导致错误的主要原因。 计算出您希望一次在屏幕上显示的最大爆炸次数,并在手工生成这些精灵N * 200之前,将它们放入数组列表ArrayList(N)中,然后在您的代码中循环引用它们。 ExplosionClass = explosionList.get(currentExplosionIndex%explosionList.size());

检查与缓存相比的图片大小,并尝试诊断缓存未命中数。 阅读面向数据的设计,它通常是其中的一部分。

另一种方法是将爆炸方式更改为不加载200个精灵并将其展开的方式(重复使用它们,还是每次爆炸时加载它们?)。 制作动画爆炸需要更少的力量,虽然它可能不那么壮观,然后你需要做一些动画。

Java也不是制作游戏的最佳语言。 你的工作水平非常高,而JVM的速度有点慢。 我的朋友在制作小游戏时遇到了类似的问题,很难制作出一个不落后的游戏。

当您将内容发送到GPU时,GPU会从GDRAM中读取信息。 如果通过调用getDrawGraphics将数据读入内存,则内存可能会从图形卡读回RAM。 CPU只能访问RAM(DRAM),GPU只能访问GDRAM。 对于板载GPU,这是不同的,因为它们没有自己的RAM,它们使用常规RAM的一部分。

要返回导致代码快速运行,请确定您拥有的硬件,然后相应地调用适当的方法(代码更改之前或之后的方法)。