In this article by Avisekhar Roy, the author of The Android Game Developer's Handbook, we will focus on the need for optimization and types of optimization with respect to games for the Android OS.
We will also look at some common game development mistakes in this article.
(For more resources related to this topic, see here.)
Let's now have a look at the types of rendering pipelines in Android.
In thecase of the 2D Android drawing system through Canvas, all the assets are first drawn on the canvas, and the canvas is rendered on screen. The graphic engine maps all the assets within the finite Canvas according to the given position.
Many times, developers use small assets separately that cause a mapping instruction to execute for each asset. It is always recommended that you use sprite sheets to merge as many small assets as possible. A single draw call can then be applied to draw every object on the Canvas.
Now, the question is how to create the sprite and what are the other consequences. Previously, Android could not support images or sprites of a size more than 1024x1024 pixels. Since Android 2.3, the developer can use a sprite of size 4096x4096. However, using such sprites can cause permanent memory occupancy during the scopes of all the small assets. Many low-configuration Android devices do not support such large images to be loaded during an application. It is a best practice that developers limit themselves to 2048x2048 pixels. This will reduce memory peak as well as significant amount of draw calls to the canvas.
Android uses OpenGL to render assets on the screen. So,the rendering pipeline for Android 3D is basically theOpenGL pipeline.
Let's have look at the OpenGL rendering system:
Now, let's have a detailed look at each step of thepreceding rendering flow diagram:
The vertex shader processes individual vertices with vertex data.
The control shader is responsible for controlling vertex data and patches for the tessellation.
The polygon arrangement system arranges the polygon with each pair of intersecting lines created by vertices. Thus, it creates the edges without repeating vertices.
Tessellation is the process of tiling the polygons in a shape without overlap or any gap.
The geometry shader is responsible for optimizing the primitive shape. Thus triangles are generated.
After constructing the polygons and shapes, the model is clipped for optimization.
Vertex post processing is used to filter out unnecessary data.
The mesh is then rasterized.
The fragment shader is used to process fragments generated from rasterization.
All the pixels are mapped after fragmentation and processed with the processed data.
The mesh is added to the frame buffer for final rendering.
Any digital game cannot be made without 2D art assets. There must be 2D assets in some form inside the game. So, as far as game component optimization is concerned, every 2D asset should also be optimized. Optimization of 2D assets means these three main things.
Each asset frame should contain only the effective pixels to be used in games. Unnecessary pixels increase the asset size and memory use during runtime.
Not all images require full data information for pixels. A significant amount of data might be stored in each pixel, depending on the image format. For example, fullscreen opaque images should never contain transparency data. Similarly, depending on the color set, images must be formatted in 8-bit, 16-bit, or 24-bit format.
Image optimization tools can be used to perform such optimizations.
The larger the amount of data compressed during optimization, the more time it takes to decompress it and load it to memory. So, image optimization has a direct effect on the processing speed.
From another point of view, creating an image atlas or sprite sheet is another way to reduce the processing time of images.
A 3D art asset has two parts to be optimized. A 2D texture part is to be optimized in the same 2D optimization style. The only thing the developer needs to consider is after optimization, the shader should have the same effect on the structure.
Rest of the 3D asset optimization entirely depends on the number of vertices and the model polygon.
It is very obvious that a large number of polygons used to create the mesh can create more details. However, we all know that Android is a mobile OS, and it always has hardware limitations.
The developer should count the number of polygons used in the mesh and the total number of polygons rendered on the screen in a single draw cycle. There is always a limitation depending on the hardware configuration.
So, limiting polygon and vertex count per mesh is always an advantage in order to achieve a certain frame rate or performance.
Models are created with more than one mesh. Using a separate mesh in the final model always results in heavy processing. This is a major effort for the game artist. Multiple overlaps can occur if multiple meshes are used. This increases vertex processing.
Rigging is another essential part of finalizing the model. A good rigger defines the skeleton with minimum possible joints for minimum processing.
It is not always possible to look into each and every performance aspect at every development stage. It is a very common practice to use assets and write code in a temporary mode and use it in the final game.
This affects the overall performance and future maintenance procedure. Here are few of the most common mistakes made during game development.
An artist creates art assets, and the developer directly integrates those into the game for debug build. However, most of the time, those assets are never optimized even for the release candidate.
This is the reason there may be plenty of high-bit images where the asset contains limited information. Alpha information may be found in opaque images.
The modernday development style does not require each and every development module to be written from scratch. Most of the developers use a predefined third-party library for common utility mechanisms.
Most of the time, these packages come with most of the possible methods, and among them, very few are actually used in games. Developers, most of the time, use these packages without any filtration. A lot of unused data occupies memory during runtime in such cases.
Many times, a third-party library comes without any editing facility. In this case, the developer should choose such packages very carefully depending on specific requirements.
In modern Android games, use of Internet connectivity is very common. Many games use server-based gameplay. In such cases, the entire game runs on the server with frequent data transfers between the server and the client device.Each data transfer process takes time, and the connectivity drains battery charge significantly.
Badly managed networking states often freeze the application. Especially for real-time multiplayer games, a significant amount of data is handled. In this case, a request and response queue should be created and managed properly. However, the developer often skips this part to save development time.
Another aspect of unmanaged connections is unnecessary packet data transferred between the server and client. So, there is an extra parsing process involved each time data is transferred.
We have already discussed programming styles and standards. The modular programming approach may increase a few extra processes, but the longer management of programming demandsmodular programming. Otherwise, developers end up repeating code, and this increases process overhead.
Memory management also demands a good programming style. In few cases, the developer allocates memory but often forgets to free the memory. This causes a lot of memory leakage. At times,the application crashes due to insufficient memory.
Substandard programming includes the following mistakes:
This is the funniest fact among ill-practiced development styles. Taking a shortcut during development is very common among game developers.
Making games is mostly about logical development. There may be multiple ways of solving a logical problem. Very often, the developer chooses the most convenient way to solve such problems.For example, the developer mostly uses the bubble sorting method for most of the sorting requirements, despite knowing that it is the most inefficient sorting process.
Using such shortcuts multiple times in a game may cause a visible process delay, which directly affects frame rate.
Android game development in 2D and 3D is different. It is a fact that 3D game processing is heavier than 2D games. However, game scale is always the deciding factor.
3D look and feel is way different than 2D. Use of a particle system in 3D games is very common to provide visual effects. In the case of 2D games, sprite animation and other transformations are used to show such effects.
Another difference between 2D and 3D look and feel is dynamic light and shadow. Dynamic light is always a factor for greater visual quality. Nowadays, most 3D games use dynamic lighting, which has a significant effect on game performance. In the case of 2D games, light management is done through assets. So, there is no extra processing in 2D games for light and shadow.
In 2D games, the game screen is rendered on a Canvas. There is only one fixed point of view. So, the concept of camera is limited to a fixed camera. However, in 3D games, it is a different case. Multiple types of cameras can be implemented. Multiple cameras can be used together for a better feel of the game. Rendering objects through multiple cameras causes more process overhead. Hence, it decreases the frame rate of the game.
There is a significant performance difference in using 2D physics and 3D physics. A 3D physics engine is far more process heavy than a 2D physics engine.
It is a common practice in the gaming industry to accept less FPS of 3D games in comparison to 2D games. In Android, the standard accepted FPS for 2D games is around 60FPS, whereas a 3D game is acceptable even if it runs at as low as 40FPS.
The logical reason behind that is 3D games are way heavier than 2D games in terms of process. The main reasons are as follows:
Android has a wide range of device configuration options supported by the platform. Running the same game on different configurations does not produce the same result.
Performance depends on the following factors.
There are many processors used for Android devices in terms of the number of cores and speed of each core. Speed decides the number of instructions that can be executed in a single cycle.There was a time when Android used to have a single core CPU with speed less than 500MHz. Now.we have multicore CPUs with more than 2GHz speed on each core.
Availability of RAM is another factor to decide performance. Heavy games require a greater amount of RAM during runtime. If RAM is limited, then frequent loading/unloading processesaffect performance.
GPU decides the rendering speed. It acts as the processing unit for graphical objects. A more powerful processor can process more rendering instructions, resulting in better performance.
Display quality is actually inversely proportional to the performance. Better display quality has to be backed by better GPU, CPU, and RAM, because better displays always consist of bigger resolution with better dpi and more color support.
We can see various devices with different display quality. Android itself has divided the assets by this feature:
It can be easily predicted that the list will include more options in the near future, with the advancement of hardware technology.
It is an odd factor in the performance of the application. More powerful CPU, GPU, and RAM demand more power. If the battery is incapable of delivering power, then processing units cannot run at their peak efficiency.
To summarize these factors, we can easily make a few relational equations with performance:
There are many technical differences between 2D and 3D games in terms of rendering, processing, and assets. The developer should always use an optimized approach to create assets and write code. One more way of gaining performance is to port the games for different hardware systems for both 2D and 3D games.
We can see a revolutionary upgrade in hardware platforms since the last decade. Accordingly, the nature of games has also changed. However, the scope of 2D games is still there with a large set of possibilities.
Gaining performance is more of a logical task than technical. There are a few tools available to do the job, but it is the developer's decision to choose them. So, selecting the right tool for the right purpose is necessary, and there should be a different approach for making 2D and 3D games.
Further resources on this subject: