26

私は非常に奇妙な問題に遭遇しました。何日も何日も答えを探してみました。私のゲームに新しいパーティクル システムが追加されましたが、遅すぎてプレイできませんでした。残念ながら、BufferedImage の変換は非常に低速です。爆発効果は、.png ファイルから読み込まれた約 200 個の白いスプライトで構成され、ランダムに回転、スケーリング、色付けされ、ランダムな速度で移動します。

トリプル/ダブルバッファリングでパフォーマンスを向上させようとしましたが、いくつかの問題に遭遇しました。

私の最初の試みは、ゲームが描かれた JPanel でした。JFrame のクラス (メイン) でバッファを設定し、ゲーム (JPanel を拡張) クラスで描画を行いましたが、Graphics g = bufferstrategy.getDrawGraphics(); は使用しませんでした。次に、描画メソッドの最後に、失われていないバッファー IF を示しました。Graphics オブジェクトを使用して描画を行っていないため、バッファーは常に「失われました」。しかし!ゲームは地獄のように速く実行されます!実用上バッファレス!しかし、どのように?

この試みは最終的にグラフィック エラーが発生せず、パフォーマンスが大幅に向上しましたが、nVidia / AMD カードのみでした。Intel GPU はこれを処理できませんでした。画面が白く点滅していました。

それで、BufferStrategyを正しく設定して使用することになりました。Game クラスは、JFrame から Graphics を取得するため、JPanel ではなく Canvas を拡張し、それを使用して JPanel で描画すると、タイトル バーの下に描画されるため、最終的にオフセットになります。それでも速い、60 FPSを修正。

さて、JFrame (メイン クラス) で BufferStrategy を作成したとき、まったく画像がありませんでした。Game クラス (Canvas) で BufferStrategy を設定することで、これを修正しました。写真は今では正しいですが、ゲーム自体はカタツムリのように遅いです. 1 回の爆発で FPS が最大 10 まで低下しますが、nVidia / AMD のみです。皮肉。古い Intel GPU でさえ 60 FPS で処理します。私は 5 ~ 6 年前の統合 Intel 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() を呼び出します。

画像の操作は BufferedImage を使用して行う必要があるため、私の意見では VolatileImage を使用してもパフォーマンスが向上しないため、この問題は非常に重要です。些細なことを見逃しているか、間違った方法でやっているに違いありません。

ハードウェアの問題ではないことを示すために、私のコンピューターの仕様を次に示します。Intel Core i7-3770k @ 4.3GHz、nVidia GTX 460、12 GB RAM

「高速」コンピュータ: Intel Core 2 Duo @ 2.7 GHz、統合 Intel グラフィックス、2 GB RAM

あなたの助けと時間をありがとう!:)

編集 VolatileImage は役に立ちますか? 私が正しく知っていれば、BufferedImagesを使用して画像操作を行う必要がありますが、それらの描画は遅いです。

4

3 に答える 3

2

確認すべき点は次のとおりです。


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:<filename>],[help],[verbose]

Toolkit.getDefaultToolkit().sync(); 実際にはモニター 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 コンストラクターが実行され、JFrame に追加された後に setBuffers を呼び出す必要があります。


super.paint(g); を呼び出す必要がないことは「比較的確実」です。それがあなたの特定の問題の原因であるかどうかはわかりませんが、ペイント機能から行を削除する必要があります。


コードは示していませんが、ランダムに移動する 200 個のスプライトを使用した爆発メカニズムがエラーの主な原因である可能性があります。一度に画面に表示したい爆発の最大数を計算し、それらのスプライトを事前に N * 200 生成し、それらを配列リスト ArrayList(N) に入れ、コードでそれらを循環的に参照します。爆発クラス = 爆発リスト.get(現在の爆発インデックス % 爆発リスト.サイズ());

于 2013-02-07T13:54:22.003 に答える
1

キャッシュと比較して画像のサイズを確認し、キャッシュ ミスの数を診断してみてください。データ指向設計について読んでください。通常はその一部です。

別の方法として、爆発の方法を 200 個のスプライトを読み込まずに分散する方法に変更することもできます (スプライトを再利用しますか、それとも爆発を行うたびに読み込みますか?)。アニメーション化された爆発を作成すると、それほど壮観ではないかもしれませんが、電力は少なくてすみます。その場合、アニメーション化を行う必要があります。

また、Java はゲームを作成するのに最適な言語ではありません。あなたは非常に高レベルで作業しており、JVM は物事を少し遅くします。私の友人も小さなゲームを作るときに似たような問題を抱えていました。

于 2013-02-07T11:05:08.473 に答える
0

GPU にデータを送ると、GPU は GDRAM から情報を読み取ります。getDrawGraphics を呼び出してデータをメモリに読み込む場合、メモリはおそらくグラフィック カードから RAM に読み込まれます。CPU は RAM (DRAM) にのみアクセスでき、GPU は GDRAM にのみアクセスできます。ただし、オンボード GPU の場合は異なります。独自の RAM が付属していないため、通常の RAM の一部を使用します。

コードをすばやく実行する原因を返すには、使用しているハードウェアを特定し、それに応じて適切なメソッド (コード変更前または変更後) を呼び出します。

于 2013-02-07T21:01:46.100 に答える