2

Suspicious fillRect() Speed

Okay so let me get this straight. Java fills rectangles by iterating through an array and changing the rgb values to a designated color. If all it does is change the color then why is Texturepaint so expensive if all it is doing is changing the integer in the array? Does changing the integer in between take time to register?

Fast fillRect() operation using setPaint(new Color());

setPaint(new Color(0,0,0));
fillRect(0,0,frame.getWidth(),frame.getHeight());

// Around 100+ fps repainting with timer set to zero milliseconds.

Slow fillRect() operation using setPaint(new TexturePaint());

setPaint(new TexturePaint(image, rectangle));
fillRect(0,0,frame.getWidth(),frame.getHeight());

// Around 20+ fps repainting with timer set to zero milliseconds.
StoneAgeCoder
  • 923
  • 1
  • 7
  • 13
  • 1
    As StackOverflowException already pointed out: There is **much** more happening "under the hood" than just filling an array. The convenience that Java offers when it comes to painting on arbitrary platforms does not come for free: The Sun/Oracle engineers worked hard to make it so easy for the user. But even **if** it was just about filling an array: In one case, the array would be filled with a constant value. In the other case, the value for each array entry would have to be *read* from the image that you are using in the `TexturePaint`. How do you load this image? – Marco13 Apr 19 '14 at 17:02
  • @Marco13 ImageIO.read(this.getClass().getResource("image.png")); Is there possibly a way to take the rgb array from the image and read it directly? I have actually tried this, but you have to fill it pixel by pixel. – StoneAgeCoder Apr 19 '14 at 17:06
  • 1
    It is possible to read the RGB array from the image directly, but only under certain conditions, and this won't help you when you want to use a `TexturePaint`. I still don't know why you are using this, but remember our discussion from http://stackoverflow.com/questions/22802772 (and I'm still "hanging on" ;-)). I still think that there should be a solution **without** `TexturePaint` that could be dramatically faster. However, you might want to try converting your image into an ARGB image directly after loading it, using the `convertToARGB` method from http://stackoverflow.com/a/22391951 ... – Marco13 Apr 19 '14 at 17:29
  • @Marco13 What a heavenly method!!!! Increased fps from 20fps to 60-70fps right on my wanted mark!!! :) I could kiss you lol jk! Post an answer and I will rate. :) thanks loads. – StoneAgeCoder Apr 19 '14 at 17:42
  • 1
    It's a utility method that I already posted in various answers. It is a rather common problem that the performance of painting PNGs (particularly ones that contain transparency) is so low when painting the image directly after loading it with `ImageIO`. If you want to, you can upvote the linked answer, or maybe http://stackoverflow.com/a/22231370 (because this one is primarily about the *performance* benefit of the ARGB format) – Marco13 Apr 19 '14 at 17:46
  • @Marco13 Cool. When you say I can use something faster than texturepaint what did you mean? I would like to make my game even faster if possible? – StoneAgeCoder Apr 19 '14 at 17:49
  • 1
    We already discussed about this in the other question that I linked above, and it already shows the warning to "avoid discussion in comments" here. The short summary again: I did not understand why you used `TexturePaint` at all, because it seemed (!) like you have only been filling *rectangles* with the `TexturePaint` - and therefore, you could just use `Graphics#drawImage` instead of `TexturePaint`. For further discussion/recommendations, a forum or chat would be more appropriate. – Marco13 Apr 19 '14 at 17:53
  • @Marco13 No need for forum chat I think I understand what you mean by using bufferedImages instead of texture paints and I will try to implement it in my code. Thanks again. – StoneAgeCoder Apr 19 '14 at 18:02
  • possible duplicate of [Java fillRect() with TexturePaint is slow](http://stackoverflow.com/questions/23118515/java-fillrect-with-texturepaint-is-slow) – Elliott Frisch Apr 25 '14 at 02:31

1 Answers1

2

As you can see from its sourcecode, Graphics delegates this functionality to subclasses.

My implementation seems to use SunGraphics2d, which again delegates it to a PixelFillPipe, which there are many implementations of. The OGLRenderer delegates this functionality to the Graphics card if possible, using OpenGL. The X11Renderer uses native X calls, like this:

native void XFillRect(long pXSData, long xgc,
                      int x, int y, int w, int h);

public void fillRect(SunGraphics2D sg2d,
                     int x, int y, int width, int height)
{
    SunToolkit.awtLock();
    try {
        long xgc = validate(sg2d);
        XFillRect(sg2d.surfaceData.getNativeOps(), xgc,
                  x+sg2d.transX, y+sg2d.transY, width, height);
    } finally {
        SunToolkit.awtUnlock();
    }
}

XRRenderer uses this code:

public synchronized void fillRect(SunGraphics2D sg2d,
                                  int x, int y, int width, int height) {
    SunToolkit.awtLock();
    try {
        validateSurface(sg2d);

        XRSurfaceData xrsd = (XRSurfaceData) sg2d.surfaceData;

        x += sg2d.transform.getTranslateX();
        y += sg2d.transform.getTranslateY();

        tileManager.addRect(x, y, width, height);
        tileManager.fillMask(xrsd);

    } finally {
        SunToolkit.awtUnlock();
    }
}

I showed you this code, because it is more than setting colors in an array. Your mileage will vary per platform and JRE.

As I don't know which renderer/fillpipe you use, I can only recommend to look at your very own code, it's not that hard.

tilpner
  • 4,351
  • 2
  • 22
  • 45
  • To be honest I don't know which renderer I am using, but I think I am using OpenGL because I call System.setProperty("sun.java2d.opengl","true");. I am kind of new to java and am rather amateurish when it comes to understanding java natives. But is there a way to possibly speed up the process done by sun to fill rectangles using texturepaint? – StoneAgeCoder Apr 19 '14 at 17:04
  • 1
    I don't know about your code, but from my experience, it mostly is about your own code. There probably would be a way to speed up the rendering for one platform, but speeding it up on all platforms is going to be tough. In the end, using TexturePaint, you're doing a little more than using Color. There's some information about this on the web. See [1](https://www.java.net/node/646433) and [2](https://community.oracle.com/thread/1267686?start=0&tstart=0). I wouldn't develop a game/graphics intensive app with Java2D. You really should try [JLWGL](http://lwjgl.org/)! – tilpner Apr 19 '14 at 17:12
  • 1
    @StoneAgeCoder: Additionally, you should profile your code, and see which implementation you use and where it's spending its time. This might help you solve your performance problems. But look at JLWGL otherwise... – tilpner Apr 19 '14 at 17:15
  • I actually read both those articles already lol :). I was getting pretty desperate. I love my java2D it is hard to let go. People keep telling me I need to use LWJGL and I have had some experience with it. I absolutely have to use Java.geo.Area with a game I am making and LWJGL from what I know does not have implementation for that or Texturepaint.(However, I am rather ignorant when it comes to OpenGL). I question too how much more performance that i could get out of filling rectangles with texturepaint using OpenGL, but once again I am ignorant when it comes to OpenGL. – StoneAgeCoder Apr 19 '14 at 17:20
  • 1
    OpenGL is a lot more lowlevel than Java2D. And this is a good thing, as it makes cost obvious and provides you with tons of flexibility. It will take time to get started with, but stuff like java.awt.geom.Area will not be a problem. Apart from the fact that someone probably already wrote a similar library, it is open-source, so you *could* copy it to your project if that's the only file you need. I'd advise against doing that and implementing a similar class for your own project yourself, therefore getting familiar with the code and you can always use the original as reference. ;) – tilpner Apr 19 '14 at 17:25
  • Okay sweet, that's really what i needed. So all I need to do is find others creations of the java.geom.Area and Texturepaint and just start over using LWJGL to create the game rather than swing and java2D? – StoneAgeCoder Apr 19 '14 at 17:29
  • 1
    You can. Though java.geom.Area doesn't look *that* complex and is possibly too tightly coupled to the rest of AWT. I would write my own. – tilpner Apr 19 '14 at 17:30
  • Thanks loads may think about switching over later on. But as of now I am going to stick with the stuff i know. One more question when you say "write your own version of area" you mean using lwjgl right? Because I would like to learn how to do this when i switch. – StoneAgeCoder Apr 19 '14 at 17:45
  • 1
    Sure, I would use LWJGL, but write all necessary utilities like Area myself. It doesn't seem like a huge thing to do. Another option is using LibGDX, if you want to stay high-level. They got good tooling, awesome platform independence (PC (Mac is a PC), Android, iOS, HTML/JS etc.) and lots of documentation. – tilpner Apr 19 '14 at 17:49
  • Did not read everything here, just a short side note: When it comes to just painting images (sprites), Java2D comes pretty close to the performance of OpenGL - particularly, when Java2D is used *properly*, and particularly because using Java2D *properly* is **much** easier than using OpenGL *properly*. I wouldn't even consider to use OpenGL for something like a tile-based 2D game. One could use OpenGL to have fancy rendering/zooming effects or so - but not because of performance, but only because *some* of these things could be supported better by OpenGL. – Marco13 Apr 19 '14 at 17:58