60

Java2D のパフォーマンスに問題があります。2D の 3D アクセラレーションを有効にする sun.java2d.opengl VM パラメータについては知っていますが、それを使用しても奇妙な問題がいくつかあります。

私が実行したテストの結果は次のとおりです。


JComponentイメージ 1 = .bmp 形式、イメージ 2 = .png 形式で 32x32 ピクセル タイルを使用して 25x18 マップを描画する

-Dsun.java2d.opengl=true なし

.BMP 画像 1 を使用した 120 FPS
.PNG 画像 2 を使用した 13 FPS

-Dsun.java2d.opengl=true を使用

.BMP 画像 1 を使用した 12 FPS
.PNG 画像 2 を使用した 700 FPS

アクセラレーションがなければ、ソフトウェアで行うすべての drawImage() で何らかの変換が行われ、.PNG の場合は FPS が大幅に低下していると想定しています。しかし、アクセラレーションを使用すると、結果が切り替わるのはなぜですか (そして、PNG は実際には信じられないほど高速に実行されます)。クレイジー!

.BMP 画像 1 は、TYPE_INT_RGB の画像タイプに変換されます。.PNG Image 2 は、TYPE_CUSTOM のイメージ タイプに変換されます。OpenGL アクセラレーションの有無にかかわらず一貫した速度を得るには、TYPE_INT_ARGB のイメージ タイプで新しい BufferedImage を作成し、この新しいイメージにイメージ 1 またはイメージ 2 を描画する必要があります。

それを実行した結果は次のとおりです。

-Dsun.java2d.opengl=true なし

.BMP 画像 1 を使用した
120 FPS .PNG 画像 2 を使用した 120 FPS

-Dsun.java2d.opengl=true を使用

.BMP 画像 1 を使用した
700 FPS .PNG 画像 2 を使用した 700 FPS

私の本当の質問は、TYPE_INT_ARGB がすべてのシステムとプラットフォームのネイティブ イメージ タイプになると仮定してよいでしょうか? この値は異なる可能性があると想定しています。最大のパフォーマンスを得るために常に新しい BufferedImages を作成できるように、ネイティブ値を取得する方法はありますか?

前もって感謝します...

4

3 に答える 3

69

あまりにも多くの Google 検索から調べて、断片をまとめることで解決策を見つけたと思います。

ここに、コメントとすべてがあります:

private BufferedImage toCompatibleImage(BufferedImage image)
{
    // obtain the current system graphical settings
    GraphicsConfiguration gfxConfig = GraphicsEnvironment.
        getLocalGraphicsEnvironment().getDefaultScreenDevice().
        getDefaultConfiguration();

    /*
     * if image is already compatible and optimized for current system 
     * settings, simply return it
     */
    if (image.getColorModel().equals(gfxConfig.getColorModel()))
        return image;

    // image is not optimized, so create a new image that is
    BufferedImage newImage = gfxConfig.createCompatibleImage(
            image.getWidth(), image.getHeight(), image.getTransparency());

    // get the graphics context of the new image to draw the old image on
    Graphics2D g2d = newImage.createGraphics();

    // actually draw the image and dispose of context no longer needed
    g2d.drawImage(image, 0, 0, null);
    g2d.dispose();

    // return the new optimized image
    return newImage; 
}

前回の投稿では、GraphicsConfiguration は、システム上で最適化されたイメージを作成するために必要な情報を保持していました。これはうまく機能しているように見えますが、Java が自動的にこれを行うと思っていたでしょう。明らかに、Java に慣れすぎることはありません。:) 私は自分の質問に答えてしまったと思います。2D ゲームに Java を使おうとしている人たちの助けになれば幸いです。

于 2008-10-13T09:04:29.147 に答える
5

これは古い投稿ですが、BufferedImage を使用せずに Swing/AWT を使用して直接描画することに関する私の発見を共有したいと思います。

3D などのある種の描画は、int[]バッファーに直接描画する方が適しています。画像を作成したら、 MemoryImageSourceなどのImageProducerインスタンスを使用して画像を生成できます。Graphics/Graphics2 の助けを借りずに、描画を直接実行する方法を知っていると仮定しています。

    /**
* How to use MemoryImageSource to render images on JPanel
* Example by A.Borges (2015)
*/
public class MyCanvas extends JPanel implements Runnable {

public int pixel[];
public int width;
public int height;
private Image imageBuffer;   
private MemoryImageSource mImageProducer;   
private ColorModel cm;    
private Thread thread;


public MyCanvas() {
    super(true);
    thread = new Thread(this, "MyCanvas Thread");
}

/**
 * Call it after been visible and after resizes.
 */
public void init(){        
    cm = getCompatibleColorModel();
    width = getWidth();
    height = getHeight();
    int screenSize = width * height;
    if(pixel == null || pixel.length < screenSize){
        pixel = new int[screenSize];
    }        
    mImageProducer =  new MemoryImageSource(width, height, cm, pixel,0, width);
    mImageProducer.setAnimated(true);
    mImageProducer.setFullBufferUpdates(true);  
    imageBuffer = Toolkit.getDefaultToolkit().createImage(mImageProducer);        
    if(thread.isInterrupted() || !thread.isAlive()){
        thread.start();
    }
}
/**
* Do your draws in here !!
* pixel is your canvas!
*/
public /* abstract */ void render(){
    // rubisch draw
    int[] p = pixel; // this avoid crash when resizing
    if(p.length != width * height) return;        
    for(int x=0; x < width; x++){
        for(int y=0; y<height; y++){
            int color =  (((x + i) % 255) & 0xFF) << 16; //red
                color |= (((y + j) % 255) & 0xFF) <<  8; //green
                color |= (((y/2 + x/2 - j) % 255) & 0xFF) ;   //blue         
            p[ x + y * width] = color;
        }
    }        
    i += 1;
    j += 1;          
}    
private int i=1,j=256;

@Override
public void run() {
    while (true) {
        // request a JPanel re-drawing
        repaint();                                  
        try {Thread.sleep(5);} catch (InterruptedException e) {}
    }
}

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    // perform draws on pixels
    render();
    // ask ImageProducer to update image
    mImageProducer.newPixels();            
    // draw it on panel          
    g.drawImage(this.imageBuffer, 0, 0, this);  
}

/**
 * Overrides ImageObserver.imageUpdate.
 * Always return true, assuming that imageBuffer is ready to go when called
 */
@Override
public boolean imageUpdate(Image image, int a, int b, int c, int d, int e) {
    return true;
}
}// end class

MemoryImageSourceImageの一意のインスタンスが必要であることに注意してください。JPanel のサイズを変更しない限り、フレームごとに新しい Image または新しい ImageProducer を作成しないでください。上記のinit()メソッドを参照してください。

レンダリング スレッドで、repaint()を要求します。Swing では、repaint()はオーバーライドされたpaintComponent()を呼び出し、そこでrender()メソッドを呼び出してから、imageProducer にイメージの更新を依頼します。Image が完成したら、Graphics.drawImage()で描画します。

互換性のある Imageを作成するには、 Imageを作成するときに適切なColorModelを使用します。私はGraphicsConfiguration.getColorModel()を使用します:

/**
 * Get Best Color model available for current screen.
 * @return color model
 */
protected static ColorModel getCompatibleColorModel(){        
    GraphicsConfiguration gfx_config = GraphicsEnvironment.
            getLocalGraphicsEnvironment().getDefaultScreenDevice().
            getDefaultConfiguration();        
    return gfx_config.getColorModel();
}
于 2016-02-10T04:17:58.793 に答える
2

Java でグラフィックス プログラミングをしようと考えていたときのことを思い出すと、組み込みのライブラリは遅いです。GameDev.Net で、深刻なことをする人は誰でもjoglのようなものを使用する必要があるとアドバイスされました

于 2008-10-13T07:01:43.793 に答える