Texture tiling on the iPad - scrolling painted backgrounds > max OpenGL ES texture size of 2048x2048px
Dead West, an iPad game which we recently introduced here uses a forced-perspective top-down view of the game world - with, what we originally thought trivial - painted background.
Because the goal of the technical design was to provide for a high quality painted background that will spread beyond the maximum texture capacity supported by the iPad (2048x2048), we needed to design a rendering engine that will be able to take multiple tiles and draw them as one seamless, scrollable background on the iPad screen. After a holy-crap-this-is-harder-than-we-thought stage, we ended up with a design that meets most of our needs, with additional possibility for optimization if needed.
The process we are currently using (Texture Tiling) shouldn't be confused with Tile Mapping. Tile maps are normally created from a texture atlas where you can draw many objects by reusing elements on the same atlas. This allows you to draw many objects on the screen using one batch (i.e. no texture swapping).
The Texture Tiling engine’s job is to take tiles from the disk and make them available in VRam. Once in VRam, we use those textures to build a bigger texture using an Frame Buffer Object (FBO). An FBO is a render-to-texture technique often used for special effects. In our context, we use it to cache previously loaded textures, so they can be unloaded when not required. The first time we create the FBO it’s a bit time consuming because we need to get all of the tiles (currently 256x256px) from disk and render them one after the other into the FBO. But further updates are much faster since we can reuse the FBO texture itself and only add to it the new textures that are coming into view.
In the above outline, the green tiles will be rendered as the visible part of the play surface moves up, and the red ones ultimately unloaded once they move out of the visible view.
This techniques is working well when we don’t have too many textures to load from disk after an update. The game plays in landscape orientation, so, for example, we can render and scroll an 8000x768px play surface with relative ease using this technique. Every time we need to load a new zone (textures that are not yet loaded from disk), we load a maximum of 3 textures (3x256==768px==iPad screen height in landscape orientation). Similarly, a 1024x8000px play surface results in a maximum load of 4 textures.
A square play surface of 2048x2048px is the natural size of a fully loaded FBO and doesn’t incur any cost at all. This is by far the best bang for the buck size vs performance.
Maps over 2048x2048px cause some performance headaches for the engine. The algorithm we’re currently using loads a full FBO width and/or height on zone change. This results in a load of between 8 and 16 textures. Whilst we’re not doing so currently, we’ll be optimizing this to only update the visible play surface area, reducing load count of textures to between 4 and 8.
Another possible optimization would be to cache loaded sections instead of querying the map for loaded sections based on the current area (we’re currently assuming this isn’t a bottleneck though).
Beyond that, we’ll assess performance and if necessary look to optimize further by asynchronously loading textures from disk when getting close to an unloaded zone. If we spit the FBO in 4 quadrants, such that if we’re viewing the top left quadrant we should start loading textures that will be on the right and bottom edges. When moving the player(s) around, this should result in an almost unnoticeable load sequence. However, we do not expect to see a noticable improvement when moving around by scrolling (which we also support) as it’s likely the user will scroll through sections too quickly. If required, we could compensate for this by not allowing scrolling outside of loaded/navigated zones or by adding fog-of-war-like effects for zones that are still loading.
We’ll report back with some performance data as and when we can -- as well as a more technical description of the engine itself once it’s matured a little.
The design and engine above was all done by our rendering/gfx/misc coder, Marc Hebert -- who can be also be found at his own blog http://shadhex.blogspot.com or more frequently on Twitter at http://twitter.com/shadhex.