Java2D 性能问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/196890/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java2D Performance Issues
提问by Consty
I'm having performance oddities with Java2D. I know of the sun.java2d.opengl VM parameter to enable 3D acceleration for 2D, but even using that has some weird issues.
我在使用 Java2D 时遇到了性能问题。我知道 sun.java2d.opengl VM 参数可以为 2D 启用 3D 加速,但即使使用它也有一些奇怪的问题。
Here are results of tests I ran:
以下是我运行的测试结果:
Drawing a 25x18 map with 32x32 pixel tiles on a JComponent
Image 1 = .bmp format, Image 2 = A .png format
在 JComponent
Image 1 = .bmp 格式,Image 2 = A .png 格式上绘制 25x18 地图,其中包含 32x32 像素图块
Without -Dsun.java2d.opengl=true
没有 -Dsun.java2d.opengl=true
120 FPS using .BMP image 1
13 FPS using .PNG image 2
120 FPS 使用 .BMP 图像 1
13 FPS 使用 .PNG 图像 2
With -Dsun.java2d.opengl=true
使用 -Dsun.java2d.opengl=true
12 FPS using .BMP image 1
700 FPS using .PNG image 2
12 FPS 使用 .BMP 图像 1
700 FPS 使用 .PNG 图像 2
Without acceleration, I'm assuming some kind of transformation is taking place with every drawImage() I do in software, and is pulling down the FPS considerably in the case of .PNG. Why though, with acceleration, would the results switch (and PNG actually performs incredibly faster)?! Craziness!
如果没有加速,我假设我在软件中所做的每个 drawImage() 都会发生某种转换,并且在 .PNG 的情况下会大大降低 FPS。但是,为什么随着加速,结果会发生变化(而 PNG 实际上执行得非常快)?!疯狂!
.BMP Image 1 is translated to an image type of TYPE_INT_RGB. .PNG Image 2 is translated to an image type of TYPE_CUSTOM. In order to get consistent speed with and without opengl acceleration, I have to create a new BufferedImage with an image type of TYPE_INT_ARGB, and draw Image 1 or Image 2 to this new image.
.BMP Image 1 被转换为 TYPE_INT_RGB 的图像类型。.PNG Image 2 被转换为 TYPE_CUSTOM 的图像类型。为了在使用和不使用 opengl 加速时获得一致的速度,我必须创建一个图像类型为 TYPE_INT_ARGB 的新 BufferedImage,并将图像 1 或图像 2 绘制到这个新图像上。
Here are the results running with that:
以下是运行结果:
Without -Dsun.java2d.opengl=true
没有 -Dsun.java2d.opengl=true
120 FPS using .BMP image 1
120 FPS using .PNG image 2
120 FPS 使用 .BMP 图像 1
120 FPS 使用 .PNG 图像 2
With -Dsun.java2d.opengl=true
使用 -Dsun.java2d.opengl=true
700 FPS using .BMP image 1
700 FPS using .PNG image 2
700 FPS 使用 .BMP 图像 1
700 FPS 使用 .PNG 图像 2
My real question is, can I assume that TYPE_INT_ARGB will be the native image type for all systems and platforms? I'm assuming this value could be different. Is there some way for me to get the native value so that I can always create new BufferedImages for maximum performance?
我真正的问题是,我可以假设 TYPE_INT_ARGB 将成为所有系统和平台的本机图像类型吗?我假设这个值可能不同。有什么方法可以让我获得本机值,以便我始终可以创建新的 BufferedImages 以获得最佳性能?
Thanks in advance...
提前致谢...
采纳答案by Consty
I think I found a solution by researching and putting bits and pieces together from too many Google searches.
我想我通过研究并将太多谷歌搜索的点点滴滴放在一起找到了一个解决方案。
Here it is, comments and all:
在这里,评论和所有:
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;
}
In my previous post, GraphicsConfiguration was what held the information needed to create optimized images on a system. It seems to work pretty well, but I would have thought Java would automatically do this for you. Obviously you can't get too comfortable with Java. :) I guess I ended up answering my own question. Oh well, hopefully it'll help some of you I've seen trying to make use of Java for 2D games.
在我之前的博文中,GraphicsConfiguration 保存了在系统上创建优化图像所需的信息。它似乎工作得很好,但我原以为 Java 会自动为你做这件事。显然,您不能对 Java 感到太舒服。:) 我想我最终回答了我自己的问题。哦,好吧,希望它会帮助你们中的一些人,我见过试图将 Java 用于 2D 游戏的人。
回答by Nick Stinemates
回答by Alex Byrth
Well, this is old post but I'd like to share my findings about direct drawing with Swing/AWT, without BufferedImage.
嗯,这是旧帖子,但我想分享我关于使用 Swing/AWT 直接绘制而不使用 BufferedImage 的发现。
Some kind of drawing, as 3D, are better done when painting directly to a int[]buffer. Once done the images, you can use an ImageProducerinstance, like MemoryImageSource, to produce images. I'm assuming you know how to perform your drawings directly, without help of Graphics/Graphics2.
直接绘制到int[]缓冲区时,可以更好地完成某种绘图,如 3D 。完成图像后,您可以使用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
Note we need unique instance of MemoryImageSourceand Image. Do not create new Image or new ImageProducer for each frames, unless you have resized your JPanel. See init()method above.
请注意,我们需要MemoryImageSource和Image 的唯一实例。不要为每个帧创建新的 Image 或新的 ImageProducer,除非您已经调整了 JPanel 的大小。参见上面的init()方法。
In a rendering thread, ask a repaint(). On Swing, repaint()will call the overridden paintComponent(), where it call your render()method and then ask your imageProducer to update image. With Image done, draw it with Graphics.drawImage().
在渲染线程中,询问repaint()。在 Swing 上,repaint()将调用重写的paintComponent(),在那里它调用您的render()方法,然后要求您的 imageProducer 更新图像。完成 Image 后,使用Graphics.drawImage()绘制它。
To have a compatible Image, use proper ColorModelwhen you create your Image. I use GraphicsConfiguration.getColorModel():
要获得兼容的 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();
}