Проблемы с производительностью Java2D - PullRequest
48 голосов
/ 13 октября 2008

У меня есть странности производительности с Java2D. Я знаю, что параметр виртуальной машины sun.java2d.opengl включает 3D-ускорение для 2D, но даже при его использовании возникают некоторые странные проблемы.

Вот результаты тестов, которые я провел:

Рисование карты 25x18 с плитками 32x32 пикселей на JComponent
Изображение 1 = формат .bmp, Изображение 2 = формат .png

Без -Dsun.java2d.opengl = true

120 кадров в секунду с использованием .BMP изображения 1
13 кадров в секунду с использованием .PNG image 2

С -Dsun.java2d.opengl = true

12 кадров в секунду с использованием .BMP изображения 1
700 FPS с использованием .PNG image 2

Без ускорения, я предполагаю, что какое-то преобразование происходит с каждым drawImage (), который я выполняю в программном обеспечении, и значительно снижает FPS в случае .PNG. Почему же при ускорении результаты переключаются (а PNG на самом деле работает невероятно быстрее) ?! Сумасшествие!

.BMP Изображение 1 преобразуется в тип изображения TYPE_INT_RGB. .PNG изображение 2 переводится в тип изображения TYPE_CUSTOM. Чтобы получить постоянную скорость с ускорением opengl и без него, мне нужно создать новый BufferedImage с типом изображения TYPE_INT_ARGB и нарисовать изображение 1 или изображение 2 в этом новом изображении.

Вот результаты, работающие с этим:

Без -Dsun.java2d.opengl = true

120 кадров в секунду с использованием .BMP изображения 1
120 кадров в секунду с использованием .PNG image 2

С -Dsun.java2d.opengl = true

700 FPS с использованием .BMP изображения 1
700 FPS с использованием .PNG image 2

Мой реальный вопрос: могу ли я предположить, что TYPE_INT_ARGB будет собственным типом изображения для всех систем и платформ? Я предполагаю, что это значение может быть другим. Есть ли какой-то способ получить исходное значение, чтобы я всегда мог создавать новые BufferedImages для максимальной производительности?

Заранее спасибо ...

Ответы [ 3 ]

57 голосов
/ 13 октября 2008

Я думаю, что нашел решение, исследовав и собрав воедино слишком много запросов в 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. :) Наверное, я ответила на свой вопрос. Что ж, надеюсь, это поможет некоторым из вас, кого я видел, пытаясь использовать Java для 2D-игр.

1 голос
/ 13 октября 2008

Из того, что я помню, когда думал о графическом программировании на Java, встроенные библиотеки работают медленно. Мне сообщили на GameDev.Net, что любой, кто делает что-то серьезное, должен использовать что-то вроде jogl

0 голосов
/ 10 февраля 2016

Ну, это старый пост, но я хотел бы поделиться своими выводами о прямом рисовании с Swing / AWT, без BufferedImage.

Некоторые виды рисования, например 3D, лучше выполнять при рисовании непосредственно в буфер int [] . После создания изображений вы можете использовать экземпляр ImageProducer , например MemoryImageSource , для создания изображений. Я предполагаю, что вы знаете, как выполнять свои рисунки напрямую, без помощи 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

Обратите внимание, что нам нужен уникальный экземпляр MemoryImageSource и Image . Не создавайте новое изображение или новый ImageProducer для каждого кадра, если только вы не изменили размер JPanel. См. init () метод выше.

В теме рендеринга задайте repaint () . На Swing repaint () вызовет переопределенный paintComponent () , где вызовет ваш метод render () , а затем попросит ваш imageProducer обновить изображение. Когда изображение готово, нарисуйте его с помощью Graphics.drawImage () .

Чтобы иметь совместимое изображение, используйте правильное 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();
}
...