Проблемы с производительностью 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 для максимальной производительности?
Спасибо заранее...
Ответы
Ответ 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();
}