Java 奇怪的缓冲策略问题-游戏仅在英特尔GPU上运行速度快

Java 奇怪的缓冲策略问题-游戏仅在英特尔GPU上运行速度快,java,performance,buffer,intel,Java,Performance,Buffer,Intel,我遇到了一个非常奇怪的问题,我日复一日地试图寻找答案。 我的游戏刚加入了一个新的粒子系统,但速度太慢,无法玩。不幸的是,BuffereImage转换非常慢。爆炸效果由大约200个从.png文件加载的白色精灵组成,它们以随机速度旋转、缩放和着色 我试图通过三重/双缓冲提高性能,但遇到了一些问题 我的第一次尝试是与JPanel的游戏是绘制。我在JFrame的类(Main)中设置了缓冲区,然后在Game(extends JPanel)类中完成了绘图,但是没有图形g=bufferstrategy.get

我遇到了一个非常奇怪的问题,我日复一日地试图寻找答案。 我的游戏刚加入了一个新的粒子系统,但速度太慢,无法玩。不幸的是,BuffereImage转换非常慢。爆炸效果由大约200个从.png文件加载的白色精灵组成,它们以随机速度旋转、缩放和着色

我试图通过三重/双缓冲提高性能,但遇到了一些问题

我的第一次尝试是与JPanel的游戏是绘制。我在JFrame的类(Main)中设置了缓冲区,然后在Game(extends JPanel)类中完成了绘图,但是没有图形g=bufferstrategy.getDrawGraphics();。然后,在绘图方法的末尾,如果缓冲区没有丢失,我将显示缓冲区。缓冲区总是“丢失”,因为我没有用它的图形对象绘制。但是这游戏跑得太快了!实际使用中没有缓冲区!但是怎么做呢

这一尝试最终没有出现图形错误,并带来了巨大的性能提升——但仅限于nVidia/AMD卡。英特尔GPU无法处理此问题,屏幕呈白色闪烁

因此,我最终正确地设置和使用了缓冲策略。 游戏类现在扩展了Canvas,而不是JPanel,因为它从JFrame获取图形,并使用它在JPanel上绘制,最终会出现偏移,就像它在标题栏下绘制一样。仍然很快,修正60 FPS

现在,当我在JFrame(主类)中创建BufferStrategy时,根本没有图片。我通过在游戏类(Canvas)中设置BufferStrategy来纠正这个问题。现在的情况是正确的,但游戏本身就像蜗牛一样慢。一次爆炸将FPS降低到约10,但仅限于nVidia/AMD。讽刺的是。即使是旧的英特尔GPU也能以60 FPS的速度处理它,我在一个有5-6年历史的集成英特尔GPU上也能以60 FPS的速度处理10000个粒子。当发生爆炸时,我的卡会达到100%的负载

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

当然,游戏一开始我就调用setBuffers()

我希望您能帮助我,这个问题非常重要,因为在我看来,使用VolatileImage不会提高性能,因为图像处理需要使用BuffereImage来完成。我打赌我错过了一些琐碎的事情,或者做了一些错事

以下是我的电脑规格,只是为了说明这不是硬件问题: 英特尔酷睿i7-3770k@4.3GHz, 英伟达GTX 460, 12GB内存

“快速”计算机: 英特尔Core 2 Duo@2.7 GHz, 集成英特尔图形, 2GB内存

谢谢你的帮助和时间!:)

编辑
你能帮忙吗?如果我知道的没错,图像处理必须使用BuffereImage来完成,但是绘制它们的速度很慢。

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

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


Java也不是制作游戏的最佳语言。您的工作级别非常高,JVM会使速度减慢一点。我的朋友们在制作小游戏时也遇到了类似的问题,很难制作出一款没有延迟的游戏。

这里有几点需要检查:


如果不知道setBuffers函数的控制台/错误输出,很难判断。Intel是否使用createBufferStrategy(2);而NV使用createBufferStrategy(3)?如果是这样,那可能是使用额外内存的问题的一部分


您试过java2d System.properties了吗?特别是用于调试的跟踪属性

仅限窗口

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:<filename>],[help],[verbose]
编辑此代码来自()

此外,您应该在画布构造函数运行并添加到JFrame之后调用setBuffers


我“相对确定”您不需要调用super.paint(g);我不知道这是否是导致您的特定问题的原因,但您应该从绘制功能中删除该行

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


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

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


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

1)两个系统上的Java版本是什么?2) 您是使用JRE运行应用程序,还是使用JDK的JRE?3) 为我们的公司提供完整编译的基准测试示例。@Andremoniy我试过的PC机:Core 2 Duo与板载Intel GPU(JRE 6),i3-3225与HD4000集成GPU(JDK 7),Celeron笔记本电脑与HD2500(JRE 7)。在我的计算机上,我在NetBeans(JDK)和使用标准jre7时得到了相同的结果。我设法减少了一点滞后,但仍然滞后。请注意,这场比赛是严重未完成的,“基准”只是射击任何单人球员的行动,使用导弹奖金滞后。阅读
    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();
        }
    }