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

#java #java-2d

Вопрос:

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

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

Рисование карты 25×18 с плитками размером 32×32 пикселя на jкомпонентном
изображении 1 = формат bmp, изображение 2 = формат. png

Без -Dsun.java2d.opengl=true

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

С помощью-Dsun.java2d.opengl=true

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

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

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

Вот результаты, полученные с помощью этого:

Без -Dsun.java2d.opengl=true

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

С помощью-Dsun.java2d.opengl=true

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

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

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

Ответ №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-игр.

Комментарии:

1. Это просто потрясающе. Это заставляет мой код работать намного быстрее. Большое спасибо.

2. Я приношу извинения за большую задержку с тем, чтобы отметить это как ответ.

3. БОЛЬШОЕ ВАМ СПАСИБО!

Ответ №2:

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

Некоторые виды рисунка, такие как 3D, лучше выполнять при рисовании непосредственно в буфер int []. После создания изображений вы можете использовать экземпляр ImageProducer, например MemoryImageSource, для создания изображений. Я предполагаю, что вы знаете, как выполнять свои рисунки напрямую, без помощи графики/Графики2.

     /**
* 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) amp; 0xFF) << 16; //red
                color |= (((y   j) % 255) amp; 0xFF) <<  8; //green
                color |= (((y/2   x/2 - j) % 255) amp; 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 и изображения. Не создавайте новое изображение или новый создатель изображений для каждого кадра, если вы не изменили размер своей JPanel. См. Метод init() выше.

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

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

Комментарии:

1. Просто интересно, какие люди отклоняют правильные ответы… Это (i) о Java2D; (ii) о высокой производительности при рисовании изображений; (iii) о рендеринге изображений, поступающих из других источников (OpenGL, собственный рендерер, программное обеспечение для визуализации и т. Д.). Так ЗАЧЕМ же понижать голос???

Ответ №3:

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

Комментарии:

1. Ну, так как самая большая потеря производительности на самом деле заключается в вызове drawImage (), я бы счел 450 вызовов с частотой 700 кадров в секунду довольно хорошими. Мне пришлось бы написать то же самое в jogl, чтобы увидеть, есть ли существенная разница, но в последних версиях Java с Java2D был достигнут большой прогресс.