Подтвердить что ты не робот

Проблемы с производительностью Java2D

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

Ниже приведены результаты проведенных тестов:

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

Без -Dsun.java2d.opengl = true

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

С -Dsun.java2d.opengl = true

12 FPS с использованием .BMP image 1
700 FPS с использованием изображения .PNG 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 FPS с использованием .BMP image 1
120 FPS с использованием изображения .PNG 2

С -Dsun.java2d.opengl = true

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

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

Спасибо заранее...

4b9b3361

Ответ 1

Я думаю, что нашел решение, исследовав и собрав воедино слишком много запросов в 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-игр.

Ответ 2

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

Ответ 3

Хорошо, это старый пост, но я хотел бы поделиться своими выводами о прямом рисовании с помощью 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 и Изображение. Не создавайте новое изображение или новый ImageProducer для каждого кадра, если вы не изменили размер своего JPanel. См. Выше метод init().

В потоке рендеринга задайте repaint(). В Swing repaint() вызовет переопределенный paintComponent(), где он вызовет метод render(), а затем попросит обновить imageProducer образ. С помощью Image done нарисуйте его с помощью 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();
}