Java2D 性能问题

发布于 2024-07-06 10:04:31 字数 1074 浏览 6 评论 0原文

我在使用 Java2D 时遇到了性能问题。 我知道 sun.java2d.opengl VM 参数可以启用 2D 3D 加速,但即使使用它也会出现一些奇怪的问题。

以下是我运行的测试结果:

在 JComponent 上绘制带有 32x32 像素图块的 25x18 地图
图像 1 = .bmp 格式,图像 2 = .png 格式

不带 -Dsun.java2d.opengl=true

120 FPS 使用 .BMP 图像 1
使用 .PNG 图像 2

时为 13 FPS 使用 -Dsun.java2d.opengl=true 时

使用 .BMP 图像 1 为 12 FPS
使用 .PNG 图像 2 的 700 FPS

在没有加速的情况下,我假设我在软件中执行的每个 drawImage() 都会发生某种转换,并且在 .PNG 的情况下会大大降低 FPS。 但是,为什么随着加速,结果会发生变化(PNG 实际上执行得更快)?! 疯狂!

.BMP 图像 1 被转换为 TYPE_INT_RGB 图像类型。 .PNG 图像 2 被转换为 TYPE_CUSTOM 图像类型。 为了在使用和不使用 opengl 加速的情况下获得一致的速度,我必须创建一个图像类型为 TYPE_INT_ARGB 的新 BufferedImage,并将图像 1 或图像 2 绘制到这个新图像。

以下是使用该命令运行的结果:

Without -Dsun.java2d.opengl=true

120 FPS using .BMP image 1
使用 .PNG 图像 2 时为 120 FPS

使用 -Dsun.java2d.opengl=true

使用 .BMP 图像 1 时为 700 FPS
使用 .PNG 图像 2 的 700 FPS

我真正的问题是,我可以假设 TYPE_INT_ARGB 将是所有系统和平台的本机图像类型吗? 我假设这个值可能会有所不同。 有什么方法可以让我获得本机值,以便我始终可以创建新的 BufferedImage 以获得最大性能吗?

提前致谢...

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.

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

Without -Dsun.java2d.opengl=true

120 FPS using .BMP image 1

13 FPS using .PNG image 2

With -Dsun.java2d.opengl=true

12 FPS using .BMP image 1

700 FPS using .PNG image 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!

.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.

Here are the results running with that:

Without -Dsun.java2d.opengl=true

120 FPS using .BMP image 1

120 FPS using .PNG image 2

With -Dsun.java2d.opengl=true

700 FPS using .BMP image 1

700 FPS using .PNG image 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?

Thanks in advance...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

铜锣湾横着走 2024-07-13 10:04:31

我想我通过研究并将大量谷歌搜索的零碎内容整合在一起找到了解决方案。

这是评论和所有内容:

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 游戏的人有所帮助。

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.

沉溺在你眼里的海 2024-07-13 10:04:31

嗯,这是旧帖子,但我想分享我关于使用 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

请注意,我们需要 MemoryImageSourceImage 的唯一实例。 不要为每个帧创建新的 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();
}

Well, this is old post but I'd like to share my findings about direct drawing with Swing/AWT, without 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 ImageProducer instance, like MemoryImageSource, to produce images. I'm assuming you know how to perform your drawings directly, without help of 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 MemoryImageSource and Image. Do not create new Image or new ImageProducer for each frames, unless you have resized your JPanel. See init() method above.

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().

To have a compatible Image, use proper ColorModel when you create your Image. I use 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();
}
坦然微笑 2024-07-13 10:04:31

据我所知,当我考虑用 Java 进行图形编程时,内置库速度很慢。 GameDev.Net 上建议我,任何认真做任何事情的人都必须使用类似 jogl 的东西

From what I remember when I was thinking about doing graphics programming in Java, the built in libraries are slow. I was advised on GameDev.Net that anyone doing anything serious would have to use something like jogl

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文