0

アルゴリズムの結果として、各ピクセルがオンザフライで計算されるアニメーション フレームをプロットする必要があります。したがって、フルスクリーン アニメーションでは、フレームごとに数百万回の操作が必要になる場合があります。可能な限り最高のリフレッシュ レート、できれば 1 秒あたり 20 ~ 30 フレーム以上を実現したいと考えています。

javaで非常に高速なフレームリフレッシュのために高度に最適化されたアーキテクチャを設計/作成する方法を教えてもらえますか?

これはプラットフォームに依存しない必要があるため、ハードウェア アクセラレーションを利用できません。コードは、中央サーバーではなく、個々のユーザーのコンピューターで実行されます。もちろん、各フレーム内のピクセル値を生成するためのアルゴリズムを単純化するという観点からこれに個別にアプローチしていますが、この質問は、使用されるアルゴリズムとは無関係に、フレーム間で高速にフレームごとにリフレッシュするためのアーキテクチャに関するものです。各フレーム内のピクセル値を生成します。たとえば、この投稿への回答では、次のような方法を探しています。

  • BufferedImage、
  • ダブルバッファリング、
  • マルチスレッド、
  • 加速されたオフスクリーン画像、
  • 他のフレーム間メソッドなど

問題をシミュレートするために、以下のサンプルコードを書きました。私のノートブック コンピューターのフル スクリーンで、以下のコードはフレームごとに 1,300,000 以上のピクセルを一意の値で個別に更新します。4 つのプロセッサと 8 ギガバイトのメモリを搭載したマシンでは、フレームあたり 500 ミリ秒かかります。以下で BufferedImage を正しく使用していないと思われます。各フレーム内のピクセル値を計算するために最終的に使用するアルゴリズムとは無関係に、以下のコードを最適化するための他のフレーム間、アーキテクチャレベルの手法について学びたいと思います。 -フレーム。コード サンプルと記事へのリンクをいただければ幸いです。

フレーム内の観点からではなく、フレームごと(フレーム間)アーキテクチャの観点から、 以下のコードを改善するにはどうすればよいですか?

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;

public class TestBuffer {
    private static void createAndShowUI() {
        TestPanel fastGraphicsPanel = new TestPanel();
        JFrame frame = new JFrame("This Needs A Faster Architecture!");
        frame.getContentPane().add(fastGraphicsPanel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setPreferredSize(new Dimension(800,600));
        frame.setResizable(true);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
    public static void main(String[] args) {java.awt.EventQueue.invokeLater(new Runnable() {  
    public void run() {createAndShowUI();}});}
}

@SuppressWarnings("serial")
class TestPanel extends JPanel {
    int w, h;
    private static int WIDTH = 700;
    private static int HEIGHT = 500;
    private static final Color BACKGROUND_COLOR = Color.white;
    private BufferedImage bImg;
    private Color color = Color.black;

    public TestPanel() {
        bImg =  new BufferedImage(WIDTH, HEIGHT,BufferedImage.TYPE_INT_RGB);
        Graphics g = bImg.getGraphics();
        g.setColor(BACKGROUND_COLOR);
        g.fillRect(0, 0, WIDTH, HEIGHT);

        Timer myTimer = new Timer(10, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if(w!=0&&h!=0){
                    if(WIDTH!=w&&HEIGHT!=h){
                        WIDTH = w; HEIGHT = h;
                        bImg =  new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
                    }
                }
                repaint();
            }
        });
        myTimer.setInitialDelay(0);
        myTimer.setRepeats(true);
        myTimer.setCoalesce(true);
        myTimer.start();
        g.dispose();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        w = getWidth();
        h = getHeight();
//      System.out.println("w, h are: "+w+", "+h);
        long startTime = System.currentTimeMillis();
        g.drawImage(bImg, 0, 0, null);
        long endDrawImageTime = System.currentTimeMillis();
        Graphics2D g2 = (Graphics2D) g;
        drawRandomScreen(g2);
        long endDrawScreenTime = System.currentTimeMillis();
        long stopTime = System.currentTimeMillis();
        long drawImageTime = endDrawImageTime - startTime;
        long drawScreenTime = endDrawScreenTime - endDrawImageTime;
        long elapsedTime = stopTime - startTime;
        System.out.println(drawImageTime+", "+drawScreenTime+", "+elapsedTime); 
    }

    private void drawRandomScreen(Graphics2D g2) {
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        for(int i=0;i<WIDTH;i++){
            for(int j=0;j<HEIGHT;j++){
                    color = new Color((int) (Math.random() * 255),(int) (Math.random() * 255),(int) (Math.random() * 255));
                    g2.setColor(color);
                    g2.drawLine(i, j, i, j);
            }
        }
    }
}
4

3 に答える 3

1

Graphics2D を介して対話する代わりに、画像データと直接対話する必要があります。これは私のコードです。私のラップトップは 20 フレーム/秒 (フルスクリーン) で実行できます。

 @SuppressWarnings("serial")
class TestPanel extends JPanel {
    int w, h;
    private static int WIDTH = 700;
    private static int HEIGHT = 500;
    private static final Color BACKGROUND_COLOR = Color.white;
    private BufferedImage bImg;
    private Color color = Color.black;

    public TestPanel() {
        bImg =  new BufferedImage(WIDTH, HEIGHT,BufferedImage.TYPE_INT_RGB);
        Graphics g = bImg.getGraphics();
        g.setColor(BACKGROUND_COLOR);
        g.fillRect(0, 0, WIDTH, HEIGHT);

        Timer myTimer = new Timer(10, new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if(w!=0&&h!=0){
                    if(WIDTH!=w&&HEIGHT!=h){
                        WIDTH = w; HEIGHT = h;
                        System.out.println("create");
                        bImg =  new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
                    }
                }
                repaint();
            }
        });
        myTimer.setInitialDelay(0);
        myTimer.setRepeats(true);
        myTimer.setCoalesce(true);
        myTimer.start();
        g.dispose();
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        w = getWidth();
        h = getHeight();
//      System.out.println("w, h are: "+w+", "+h);
        long startTime = System.currentTimeMillis();
        long endDrawImageTime = System.currentTimeMillis();
//        Graphics2D g2 = (Graphics2D) g;
        drawRandomScreen(bImg);
        g.drawImage(bImg, 0, 0, null);
        long endDrawScreenTime = System.currentTimeMillis();
        long stopTime = System.currentTimeMillis();
        long drawImageTime = endDrawImageTime - startTime;
        long drawScreenTime = endDrawScreenTime - endDrawImageTime;
        long elapsedTime = stopTime - startTime;
        System.out.println(drawImageTime+", "+drawScreenTime+", "+elapsedTime); 
    }

    private void drawRandomScreen(BufferedImage image) {

        final int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
        final int width = image.getWidth();
        final int height = image.getHeight();

        long startTime = System.currentTimeMillis();
//        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
//                RenderingHints.VALUE_ANTIALIAS_ON);
        Random r = new Random();
        for(int i=0;i<width;i++){
            for(int j=0;j<height;j++){
                    color = new Color(r.nextInt(255),r.nextInt(255),r.nextInt(255));
                    int pos = j*width+i;
                    pixels[pos] = color.hashCode();

            }
        }
        long stopTime = System.currentTimeMillis();
        System.out.println("time "+(stopTime-startTime));
    }
}
于 2013-06-26T03:37:01.320 に答える
1

これが新しいコードです。私のラップトップでは、フルスクリーンは 150 フレーム/秒で実行できます。ご覧のとおり、関数 drawRandomScreen の実行時間は drawImageTime の 1/2 時間です。

private void drawRandomScreen(BufferedImage image) {

    final int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
    final int width = image.getWidth();
    final int height = image.getHeight();

    long startTime = System.currentTimeMillis();
    Random r = new Random();
    int size = pixels.length;
    //swap 100 times
    int maxPixelsSwap = 1000;
    size-=maxPixelsSwap;
    for (int i = 0; i < 100; i++) {
        int src = r.nextInt(size);            
        int des = src+r.nextInt(size-src);
        int swapsize = r.nextInt(maxPixelsSwap); //maximium
        int[] temp = new int[swapsize];
        System.arraycopy(pixels, des, temp, 0, swapsize);
        System.arraycopy(pixels, src, pixels, des, swapsize);
        System.arraycopy(temp, 0, pixels, src, swapsize);

    }

    size = pixels.length;
    int randomTimes = size/10; //only change 10% of pixels
    size--;

    for (int i = 0; i < randomTimes; i++) {
        pixels[r.nextInt(size)]=r.nextInt();
    }
    long stopTime = System.currentTimeMillis();
    System.out.println("time "+(stopTime-startTime));
}
于 2013-06-28T14:13:16.893 に答える