Saturday, April 2, 2011

Response: Debugging High CPU Usage

Answer to: a comment posted on Double Buffering and Timed Rendering and on Game State Machine about 100% CPU usage with those samples.

Disclaimer: If you modified the examples, I can't help you, lol just kidding, it's a silly thing to have to say but try just redownloading and running the examples without any modifications.

The answer is a bit complicated, high CPU usage might be due to a few things.

Note: One thing I did not do that might make a difference is to use the createCompatibleImage method, this might make all the difference but I've never seen it have any effect. Or you could try using a VolatileImage it has it's own issues though, and I've never seen it have any effect either. If that doesn't work turn on java2D logging using the system properties see link below and look to see how long drawing operations are taking.

Another Note: If not on windows, try turning on opengl rendering and make sure it's actually on, you can verify it by using the "True" string for turning it on, with the capital "T" it will output whether opengl or directx rendering are being used.


Java Version Issues
Using your own buffer and repaint should work decently in Java versions on windows greater than 1.4.1 update 2, when directx support was added and turned on by default. Opengl support was added as of 1.5 but is not turned on by default on any platform, so you'd want to try fiddling with some of these properties. See System Properties docs

Problem with Repaint
In earlier versions of Java using repaint would result in tearing issues, because old swing versions would be not be double buffered. Thus the example with their own double buffering.

Hardware Acceleration Issues
No hardware acceleration will be available if you don't have some type of graphics card or if java has issues with the drivers, so it will only go as fast as the underlining computers CPU can handle in that case. Another issue using BufferedImages is the hardware acceleration can be lost if the bufferedimage raster is modified. Another choice is to use the Canvas approach with BufferStrategy which I have seen much much better performance than either creating a buffer myself .

I haven't seen 100% constant CPU usage with this example myself. If repaint works I do suggest going with what you know works well until you find something better. In the end if looks good and feels good for your game then it doesn't matter which method you use.

Things I tried
Trying to duplicate the 100% CPU usage with the Double Buffering and the Game State Machine examples was unsuccessful. I tried turning off hardware acceleration -Dsun.java2d.noddraw=True still performed as expected. I didn't change any of the code so it was left as is, still no 100% CPU usage, on an old 2.66 GHZ pentium 4 with a NVIDIA GeForce 8400 GS graphics card and tested with Java versions 1.5, 1.6, and 1.7ea without issue.

The only thing I could think of that would cause 100% CPU usage with these examples in their unmodified state is if the TimerTask run method takes more than 16 milliseconds. That would bog down the timer with continuous tasks needing to be fired since the java.awt.Timer is a single threaded queue based task scheduler. I was able to bog down the Timer and see high cpu usage. Haven't tried forcibly making the bufferedimage non-hardware accelerated yet. You could implement your own timer and see if that makes a difference.

I am meaning to work up a new snippet with better rendering methods, and updated code. The BufferStrategy approach link above is a good alternative and much better frame rates. Give that a try if your having trouble, I would also like to know if that works better for most systems. It's been a long time since I did those snippets back in 2007 I have learned a lot since then, still there is much much more to learn.

One Last Thing
I do remember that there was some issues I had on windows with a graphics card installed that slowed my rendering significantly, those were the options in the Display Properties -> Settings (tab) -> Advanced -> Troubleshoot (tab) the Hardware Acceleration, I have seen it have interesting effects at different levels with some of my Java games projects.

For further help just ask and you shall receive, of course more details next time would help tremendously.

Good Luck!

Tuesday, December 21, 2010

Scrolling Multi-layered Map

Only the top-left most part of the
generated map
The next logical step for a tilemap is to add scrolling. The problem is you don't want to be drawing every tile in the map all the time so our basic approach will not work for larger and larger maps. We need a solution that will only ever loop through and draw the tiles that are going to be visible. This snippet makes sure that only the visible tiles in the background layer are drawn. Its limited though because all the foliage tiles are drawn, however a quick bounds check might be enough to skip the expensive render calls but we would want to find a way to remove those loops entirely.

See Rendering Multi-layered Map and Rendering Basic Tiled Maps for details on other pieces in this snippet.

What's Different
Larger section of the same map
Using scrolling
The map generation is pretty much the same aside from keeping around the mapping of integers to tiles the method discussed the previous map snippets. Sorry to say it's not a very good implementation but that is an area that can be improved upon in a future snippet :) . It works for the time being.

A new update method has been added and the unused passed variable in the render thread is now being used to passed the number of seconds passed each frame to the update method. NOTE: this isn't the most accurate way of time keep as it is directly effected by frame rates and could cause jittery and inconsistent animation. But then we're not worried about animation here so it doesn't really matter. I added a keylistener to track when and which arrow keys are down in order to scroll the map. The update method handles the actual calculations to move around the map based on the keys that are pressed, it's just simple input handling nothing special. Here is the update method implementation.

/**
  * This method will move our position in the map.
  *
  * @param d
  */
 protected void update(double d) {

  Dimension screenSize = getPreferredSize();

  if (up) {
   y -= scrollSpeed * d;
  } else if (down) {
   y += scrollSpeed * d;
  }

  if (right) {
   x += scrollSpeed * d;
  } else if (left) {
   x -= scrollSpeed * d;
  }

  //clamp display area to inside the map
  if(x < 0) {
   x = 0;
  }

  if(y < 0) {
   y = 0;
  }

  // handle what happens when we reach over the edge of the map
  if(x + screenSize.width > mapSize.width) {
   x = mapSize.width - screenSize.width; //revert to last position
  }

  if(y + screenSize.height > mapSize.height) {
   y = mapSize.height - screenSize.height; //revert to last position
  }
 }

So there is some extra handling if we try and scroll outside the visible area of the map. There is bottom and left edge handling which works pretty accurately. So no matter what our speed is we ensure that as soon as the visible area is outside the map we will reset them to be exactly at the edge and never beyond.

The rendering method has been altered heavily for this snippet as it now needs to be able to handle a larger map.

/**
  * We only want to render what is currently visible on screen, so we need to
  * take into account the current starting x,y coordinates which will be used
  * to scroll through the map during updates. Our world object holds the
  * tiles in terms of a single index value
  *
  * @param g
  */
 protected void render(Graphics2D init) {
  // draw only the tiles that are visible, the visible area is given by
  // the size of the component and the current x,y starting position on
  // screen.
  Dimension screenSize = getPreferredSize();
  Graphics2D g = (Graphics2D) init.create();
  g.setColor(Color.black);
  g.fillRect(0, 0, screenSize.width, screenSize.height);
  g.translate(-x, -y); // move the graphics object to the new position

  // this will give us the row in the map grid we are starting from
  int sRow = (int)y / tileHeight;
  int sCol = (int)x / tileWidth;
  int cols = 1 + screenSize.width / tileWidth; // total number of columns
  int rows = 1 + screenSize.height / tileHeight; // total rows
  int row, col;
  Tile t;
  int index = 0;
  for (int i = 0; i < rows * cols; ++i) {
   row = i / cols;
   col = i - row * cols;

   // transform to new position in the map
   row += sRow;
   col += sCol;

   index = row * (mapSize.width/tileWidth) + col;

   // we are outside the bounds of the map, safety check
   if(index >= tiles.size()) {
    break;
   }

   // draw the tile at the given index
   tiles.get(index).render(g);
  }

  // keeping it simple, and fast
  // TODO: Optimize to only loop over visible objects in the world
  for (Tile tile : world) {
   tile.render(g);
  }
  g.dispose();

  // draw debug string on top of everything
  g = init;
  g.setColor(Color.black);
  g.drawString(String.format("%.2f (%d), %.2f (%d)", x, sCol,y, sRow), 10, 10);
 }

One of the benefits of using the HashMap approach is evident here we can pull the tile to draw from this map with O(1) constant time lookup. This is huge here because we don't need to rebuild a list of all visible tiles nor do we have to partition the map in any way. We've kept the background tiles in a single collection increasing memory efficiency and locality, hopefully. After working with Java's HashMaps you begin to wonder what you would ever do without them. 

How it works?
Going back to a previous snippet "Rendering Basic Tiled Maps" I outlined a way of indexing tiles from top-left to bottom-right.

+-x 0   1   2
y .---.---.---.
0 | 0 | 1 | 2 |
  |---|---|---|
1 | 3 | 4 | 5 |
  |---|---|---|
2 | 6 | 7 | 8 |
  '---^---^---'
Basically we will create an index like this for the visible area which is only a subset of the larger map.
+-x 0   1   2
y .---.---.---.
0 | 0 | 1 | 2 |      0   1
  |---@@@@@@@@@ => .---.---.
1 | 3 @ 4 | 5 @  0 | 0 | 1 |
  |---@---|---@    |---|---|
2 | 6 @ 7 | 8 @  1 | 2 | 3 |
  '---@@@@@@@@@    '---^---'
    Full Map        Visible
                     Area
We will iterate through the indexes of the visible area grid 0,1,2,3 and for each column and row of that visible area which will be 0-0, 1-0, 0-1 and 1-1 respectively we will add an column and row offset to them based on the visible areas position in the larger map. The offset in the example above would be 1,1 now the corresponding indexes into the larger map will be (0+1) * 3 + (0 + 1) = 4 then (0+1) * 3 + (1+1) = 5 now we move down one row (1+1) * 3 + (0+1) = 7 and the last is (1+1) * 3 + (1+1) = 8. Pretty neat huh, well at least I thought so when I came up with it years ago while making my first small game in Visual Basic 5, yeah it's been a long time since then.

Where's the Snippet
So here it is, a bit lengthy now at 639 lines but it's all in a single source file if you've been following along with the previous examples it shouldn't be hard to pickup and use these concepts in your own way, if you find them useful. I of course would love to hear more opinions on this method or better implementations. Always looking for more efficient ways to do things and squeeze every nanosecond out of that CPU. Happy Holiday coding everyone!