What might make Graphics2D.drawImage() slow?

I’m developing a physics sandbox game. I can easily fill the whole screen with rotated, scaled, textured boxes without noticing any FPS drops or excessive CPU usage, and drawing the character without a rotate transformation is fast.

However, if I rotate the character image, or scale it, CPU usage goes through the roof and there is a small but noticable FPS decrease. Using a profiler, I determined that drawImage() was the problem. As far as I can tell the character and texture images are being drawn the same way… It’s really confounding.

From the character:


private VolatileImage character = Util.getGraphicsConfiguration().createCompatibleVolatileImage(200, 200, Transparency.BITMASK); //I tried using Transparency.TRANSLUCENT
[...]
    public void draw(Graphics g) throws BuildismException
    {
        updateImage(); //This line draws the appropriate sections of the player's skin in the right locations. If I remove it, the character is a white box, but drawing it is still slow!
        Vec2D pos = getPart().getPosition().worldToScreen(Main.getView());
        int w = (int) (200*(Main.getView().scaleFactor/8));
        int h = (int) (200*(Main.getView().scaleFactor/8));
        int imgX = (int) pos.getX() - w/2;
        int imgY = (int) pos.getY() - h/2;
        AffineTransform orig = ((Graphics2D)g).getTransform();
        AffineTransform rotate = AffineTransform.getRotateInstance(-Math.toRadians(getPart().getRotation()), pos.getX(), pos.getY());
        ((Graphics2D)g).setTransform(rotate);
        g.drawImage(character, imgX, imgY, w, h, null); //I tried using a scale transform here, didn't help
        ((Graphics2D)g).setTransform(orig);
    }

For textured boxes (yes, I know this code is messy and unoptimized… but it works faster than the much simpler code for drawing a character!)


    public void setTexture(BufferedImage t)
    {
        if(t != null)
        {
            VolatileImage i = Util.getGraphicsConfiguration().createCompatibleVolatileImage(t.getWidth(), t.getHeight(), Transparency.TRANSLUCENT);
            Graphics2D g2 = (Graphics2D) i.getGraphics();
            Composite old = g2.getComposite();
            g2.setColor(transparent);
            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OUT));
            g2.fillRect(0, 0, t.getWidth(), t.getHeight());
            g2.setComposite(old);
            g2.drawImage(t, 0, 0, null);
            texture = i;
        }
        else
            texture = null;
    }

[...]

            Graphics2D g2 = (Graphics2D) g;
            Vec2D worldPos = new Vec2D(body.getPosition());
            Vec2D imgPos = worldPos.add(new Vec2D(-getVec2DProp("Size").getX(), getVec2DProp("Size").getY()).mul(0.5)).worldToScreen(Main.getView());
            Vec2D rotateCenter = worldPos.worldToScreen(Main.getView());
            AffineTransform original = g2.getTransform();
            AffineTransform scale = AffineTransform.getScaleInstance(scaleDimensionForScreen(texture.getWidth(), getVec2DProp("Size").getX()), scaleDimensionForScreen(texture.getHeight(), getVec2DProp("Size").getY()));
            AffineTransform rotate = AffineTransform.getRotateInstance(-body.getAngle(), rotateCenter.getX(), rotateCenter.getY());
            g2.setTransform(rotate);
            g2.transform(scale);
            g2.drawImage(texture, (int) (imgPos.getX() / ((Main.getView().scaleFactor * getVec2DProp("Size").getX())/texture.getWidth())), (int) (imgPos.getY() / ((Main.getView().scaleFactor * getVec2DProp("Size").getY())/texture.getHeight())), null); //ew
            g2.setTransform(original);

Does anyone see a problem?

Finally solved this yesterday! For some reason drawing a VolatileImage with bitmask transparency mode is not hardware accelerated, while drawing one with translucent transparency mode is.

That was actually the first thing on my mind, when I read your original post, but I saw:

createCompatibleVolatileImage(200, 200, Transparency.BITMASK); //I tried using Transparency.TRANSLUCENT

Hy guys!

I have this problem as well. :S

I try to draw a part of an image using this code:

g.drawImage(Main.getImageCache().getImage("tileset_" + Main.getMapManager().getMapData().getTileset()), xPos, yPos, xPos + 64, yPos + 32, tileXOffset(firstTileImg), tileYOffset(firstTileImg), tileXOffset(firstTileImg) + 64, tileYOffset(firstTileImg) + 32, null);

The image is a VolatileImage. It draws the image good, it’s too slow, using 50% CPU all the time while drawing 60-70 images.

EDIT: Sometimes it’s using 0-5% CPU, sometimes using 50%. I profiled it when it using 50%, and I figured that the drawn method makes it that bad.

I create the VolatileImage as a TRANSCULENT image.

Anyone can help pls?

(I know I’m begging for help in my first post, but I’ll be online. I love this site.)

EDIT2:

Guys I tired to profile again, here is the result:

http://img638.imageshack.us/img638/1107/errorijv.jpg

I’m not an expert, but I would guess it is not hardware accelerated. Can you verify it is hardware excelerated? I saw something in the Java2d section here, about logging for the graphics, to determine if it is hardware accelerated.

Thanks for your answer.

I found that if the BufferStrategy’s content lost before the first draw, this will happen. I did this to fix it:

            //If at the first run the mainFrm.getBufferStrategy().contentsLost()
            //returns true then the CPU usage of the program will jump up to 50%
            //this doesn't happens on every PC and it must be a JRE Bug.
            //We just do a lame fix here, but I don't have any other idea.
            if (mainFrm.getBufferStrategy().contentsLost() && isFirstBufferCheck) {
                mainFrm.getBufferStrategy().dispose();
                mainFrm.createBufferStrategy(2);
            }

            if (isFirstBufferCheck) {
                isFirstBufferCheck = false;
            }

This really sucks, but I can’t do any better right now.

As stated in the API, there is a certain way of using BufferStrategy in your game loop:


mainFrm.createBufferStrategy(2);
BufferStrategy strategy = mainFrm.getBufferStrategy();

while(!done) {
    //GAME CODE
    
    do{
        do{
            Graphics g = strategy.getDrawGraphics();
            //RENDERING
            g.dispose();
        }while(strategy.contentsRestored());
        
        strategy.show();
    }while(strategy.contentsLost());
}

Well thanks a lot, this is usefull.