3

opengl および direct3d パイプラインが無効になっている場合 (-Dsun.java2d.d3d=false および -Dsun.java2d.opengl=false で vm を呼び出すことにより)、Java2d を使用してスムーズな動きやアニメーションを取得する方法がわかりません。

以下の簡単で汚いコードは、私の問題を示しています。画面上を移動するボックスを描画します。ボックスの位置は 1 秒間に約 60 回更新され、画面は可能な限り何度も再描画されます。BufferStrategy クラスを使用してダブル バッファリングを実装します。フリップは「bs.show();」で行われます。

コード (エスケープを押して終了):

import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferStrategy;

public class FluidMovement {
    private static volatile boolean running = true;
    private static final int WIDTH = 500;
    private static final int HEIGHT = 350;

    public static void main(String[] args) {
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        GraphicsDevice gd = ge.getDefaultScreenDevice();
        GraphicsConfiguration gc = gd.getDefaultConfiguration();

        Frame frame = new Frame(gc);
        frame.setIgnoreRepaint(true);
        frame.setUndecorated(true);
        frame.addKeyListener(new KeyAdapter() {
            @Override public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                    running = false;
                }
            }
        });
        frame.setSize(WIDTH, HEIGHT);
        frame.setVisible(true);
        frame.createBufferStrategy(2);
        BufferStrategy bs = frame.getBufferStrategy();
        long nextTick = System.nanoTime();
        class Rect {
            int dx = 2, dy = 1, x = 0, y = 0;
        }
        Rect rect = new Rect();

        Graphics g;
        while (running) {

            if (System.nanoTime() > nextTick) {
                rect.x = (rect.x + rect.dx) % WIDTH;
                rect.y = (rect.y + rect.dy) % HEIGHT;
                nextTick += 1000000000 / 60;
            }

            g = bs.getDrawGraphics();
            g.setColor(Color.BLACK);
            g.fillRect(0, 0, WIDTH, HEIGHT);
            g.setColor(Color.WHITE);
            g.fillRect(rect.x, rect.y, 10, 10);
            g.dispose();
            bs.show();
        }
        bs.dispose();
        frame.dispose();
    }

}

このコードを「java FluidMovement」で通常どおり実行すると、jvm が direct3d/directdraw パイプラインを使用するため、シルクのようにスムーズに実行されます (時折のテアリングを除く)。このコードを「java -Dsun.java2d.d3d=false -Dsun.java2d.opengl=false FluidMovement」で実行すると、ひどく不安定になります。

direct3d または opengl パイプラインが使用されているとは想定できません。パイプラインは、私が試した 3 台のマシンのうち 2 台では機能しません。Windows 7 を実行している専用のグラフィックスを備えたマシンでのみ機能しました。ボックスをスムーズに動かすことはできますか、それとも JOGL のような低レベル アクセスのライブラリを使用する必要がありますか?

ノート:

  • フレームレートは問題ではありません。どちらの場合も (パイプラインが有効な場合と無効な場合)、アプリケーションは 300 fps をはるかに超えて実行されます。パイプラインが有効になっているときに、vsync を強制的にオフにしました。
  • Toolkit.getDefaultToolkit().sync() を試しました
  • さまざまな種類のループを試しましたが、動きが本当にスムーズではありません。フレームレートを固定しても、同じような途切れが見られます。
  • フルスクリーン排他モードでフレームを実行してみました。
  • 3 つまたは 4 つのバッファーを使用してみました。
4

2 に答える 2

3

いろんなものが飛び出して怖がる…

  1. スレッド/スイング契約を尊重していません。UI へのすべての更新は、イベント ディスパッチ スレッドを使用して行う必要があります。実行時間の長いコードやブロッキング コードはすべて、バックグラウンド スレッドで実行する必要があります。
  2. あなたの「アニメーション ループ」は、何もせずに多くの CPU 時間を消費しています。スレッドはサイクル間でスリープする必要があります (または、少なくとも、何かが変更されたときにのみペイントする必要があります)。これにより、システムの全体的な負荷が軽減されます。

いくつかの解決策を試しました。

「重大な」問題はありませんでしたが、これらは非常に単純な例であり、デフォルトの JVM オプションで一般的にパフォーマンスが向上しました。

バッファリング戦略

これは基本的にあなたが持っていたものです.EDTに慣れて、使用していたバッファ戦略を使用してください.

public class SimpleAnimationTest {

    private boolean running = true;
    private Rectangle box = new Rectangle(0, 90, 10, 10);
    private int dx = 4;
    protected static final int WIDTH = 200;
    protected static final int HEIGHT = 200;

    public static void main(String[] args) {
        new SimpleAnimationTest();
    }

    public SimpleAnimationTest() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame();

                frame.setIgnoreRepaint(true);

                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.setSize(WIDTH, HEIGHT);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);

                frame.createBufferStrategy(2);
                final BufferStrategy bs = frame.getBufferStrategy();

                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        long tock = 1000 / 60;
                        while (running) {

                            box.x += dx;
                            if (box.x + box.width > WIDTH) {
                                box.x = WIDTH - box.width;
                                dx *= -1;
                            } else if (box.x < 0) {
                                box.x = 0;
                                dx *= -1;
                            }

                            Graphics2D g = (Graphics2D) bs.getDrawGraphics();
                            g.setColor(Color.BLACK);
                            g.fillRect(0, 0, WIDTH, HEIGHT);
                            g.setColor(Color.WHITE);
                            g.fill(box);
                            g.dispose();
                            bs.show();
                            try {
                                Thread.sleep(tock);
                            } catch (InterruptedException ex) {
                            }
                        }
                        bs.dispose();

                    }
                }).start();

            }
        });
    }
}

ダブルバッファスイングコンポーネント

public class SimpleAnimationTest {

    private Rectangle box = new Rectangle(0, 90, 10, 10);
    private int dx = 4;

    public static void main(String[] args) {
        new SimpleAnimationTest();
    }

    public SimpleAnimationTest() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new SimplePane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class SimplePane extends JPanel {

        public SimplePane() {

            setDoubleBuffered(true);

            Timer timer = new Timer(1000 / 300, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    box.x += dx;
                    if (box.x + box.width > getWidth()) {
                        box.x = getWidth() - box.width;
                        dx *= -1;
                    } else if (box.x < 0) {
                        box.x = 0;
                        dx *= -1;
                    }
                    repaint();
                }
            });
            timer.setRepeats(true);
            timer.setCoalesce(true);
            timer.start();
        }

        @Override
        protected void paintComponent(Graphics g) {
            Graphics2D g2d = (Graphics2D) g.create();
            super.paintComponent(g2d);
            box.y = (getHeight() - box.height) / 2;
            g2d.setColor(Color.RED);
            g2d.fill(box);
            g2d.dispose();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }
    }
}
于 2012-11-23T03:24:49.890 に答える