The past couple of days I have been polishing the 2D map rendering in Chunky. The 2D map is a feature that has been mostly neglected since I started working on the 3D rendering.
Originally Chunky was only a 2D mapping program. I created Chunky to explore my own small Minecraft worlds and look for hidden treasures near my mineshafts. The default rendering mode was the “layer” mode, in which a single layer of blocks is rendered top-down. In order to allow quickly switching between layers I designed Chunky store most of the loaded chunk data and only throw it away when out of memory. This was done to avoid reading from the hard disk whenever a new layer was loaded.
Nowadays Chunky is used quite differently and the requirements for the 2D map have changed. The 2D map is now mostly a tool for selecting chunks to be rendered in 3D. This means that the layer mode is seldom used — the default “surface” mode is mainly used for chunk selection. In addition much larger views have to be rendered by the 2D map because when selecting a portion of the map to render you usually want a good overview of the entire area to be rendered.
Rendering a Minecraft map covering a large area is a significant challenge, especially for interactive applications. The number of chunks that need to be loaded is immense. If the map is zoomed out as far as possible (equivalent to one chunk per pixel) Chunky, at the default window size, will display about half a million chunks. Simply loading all those chunks takes significant time even if you are loading from a fast SSD drive. The memory requirements for storing the map are also massive. Just storing color values for each block in such a map would require 480 megabytes. There are additional overheads which bring it up to about one gigabyte of data stored for just the 2D map.
One thing I did to improve the efficiency of the 2D map was to remove caching of chunk data. The chunk data includes information about each block in a chunk, biome metadata, block metadata etc. The memory required for the complete chunk data set is much larger than just the 2D map rendering. Removing this caching may lead to many extra disk reads when creating a 3D scene, but it noticeably improved the loading speed for the 2D map.
I also removed pre-rendering of background maps such as the layer map, biome map (seen above), or cave map. Only the currently viewed map is stored by Chunky. This means that chunks need to be re-loaded if the map mode is changed, however this happens so seldom that the memory savings are likely worth more than the extra cost of switching to another map mode.
In addition to removing the caching of chunk data and background maps, I improved the way the map is updated. The 2D map is interactive, meaning that it is redrawn whenever a chunk is loaded for the first time or whenever a chunk is re-loaded because someone changed it on disk. It used to be that most of the map was redrawn if something changed but now it is supposed to redraw only the region or chunk that was updated, depending on the zoom level. To test the smarter redrawing I made the map renderer highlight each pixel that is drawn with a color indicating in which rendering pass it was drawn. It looked kinda funky:
During this process I was able to fix some tearing issues that were caused by bugs in the previous map redrawing code.
Another not so noticeable but important feature of the 2D map is that it loads some chunks outside of the current map view, this gives a margin of already-loaded chunks around the current view which makes panning behave a lot smoother than it otherwise would.
The 2D map should be more responsive in the next release. I have implemented several smaller usability improvements in addition to the above performance improvements.
There are still improvements that could be made to the 2D map to make it even more responsive. These have not been implemented yet, but I might return to it in the future and add some of these features:
- Cache each region at max zoom level on disk
- Don’t load heightmap data at max zoom level
- Use bloom filters instead of chunk sets
- Fine tune surface rendering mode