UnigineScript
The Language
Core Library
Engine Library
Node-Related Classes
GUI-Related Classes
Plugins Library
High-Level Systems
Samples
C++ API
API Reference
Integration Samples
Usage Examples
C++ Plugins
Content Creation
Materials
Unigine Material Library
Tutorials

Optimization for Mobile Devices

When starting to develop for any mobile devices and tablets, keep in mind that they have limited computing power and memory for storing you project resources. These are two main bottlenecks that hamper your application performance. Because of that, you need to optimize all your content to be fully enhanced for the target platform or a specific device.

Scene Creation Workflow

First of all, create and set up your world on the desktop machine. The typical workflow is as follows:

  1. Launch OpenGL application with simplified shaders (see details below).
  2. Add and set up objects in the world.
  3. Add and set up dynamic light sources.
  4. Bake all light sources into light maps. After that, delete unnecessary lights.
  5. Optimize your content and compress all textures.
  6. Optimize rendering of the world.

Your scene is ready. Right now you can deploy your application project on the real mobile device to test its performance.

Application Testing

Rendering performance on the development machine is not equal to performance on the actual device. Of course, the memory bandwidth and processing capabilities of desktop computers are higher. When your application will be deployed, your code will run much slower and memory storage requirements will be much stricter.

Notice
It is not possible to render the Unigine viewport image in simulators. After your scene is set up, test your application performance directly on the target device.

Remote Access to Console

Unigine makes it possible to control the application that has been run on the device by giving the remote access to console. This feature is available for both iOS and Android devices. Instead of re-uploading the application, you can directly change any rendering or other option by passing console commands.

  • Connection to the device is established via Telnet protocol (use PuTTY or any other Telnet client). The default TCP port is 8888.
  • To close the session, use logout command.
  • Remote access to the console is available only for development version of Unigine applications.

Simple Trick to Increase App Performance

If your application does not run smoothly even though you followed all optimization guidelines, decrease the application resolution set as a CLI argument or via the console. You can use one of the following:

  • video_mode for fixed resolution
  • custom video_width together with video_height

All mobile devices support upscaling of the display buffer for the target screen on the hardware level. Thus, if you set a resolution too high, a native one will be automatically used without any performance penalty. If you set the resolution that is lower than the target screen size, your application will still deliver enough visual quality but at the lower cost.
If you set small screen resolution for your application, you can also freely downscale used textures.

Optimize Your Code

Unigine allows to reuse the same codebase even if you are targeting different platforms. However, the issue of code efficiency is of paramount importance for mobile applications.

  • Make your code fast and computationally easy. Avoid using complex arithmetic operations such as log, pow, exp, sin, cos, etc.
  • Create as few objects as possible.
  • Multithreading is usually pointless, as it is rarely supported on the hardware level.

Mobile Code and Controls

As for mobile controls that support user input, the difference between desktop machines and mobile devices is that the latter do not have a keyboard or a mouse. Instead, they use touch screen functionality. If you want your code to be both PC- and mobile-compatible, use the HAS_APP_TABLET define for your mobile code (regardless of the platform), as in the following example.

Source code (UnigineScript)
#ifdef HAS_APP_TABLET
// Put your mobile-specific code here

void getTouch() {
	// Get the number of touch points
	forloop(int i = 0; engine.tablet.getMouseCounter()) {
		// Get X and Y coordinates of a touch point
		int x = engine.tablet.getMouseX(i);
		int y = engine.tablet.getMouseY(i);
		do_something();
	}
}

void showKeyboard() {
	if(engine.tablet.getKeyboardShow() == 0) {
		// Show a screen keyboard
		engine.tablet.setKeyboardShow(1);
	} 
}

#else
// Put your PC-specific code here, if any
#endif

Code for Android

To code Android-specific logic, you can use the _ANDROID define:

Source code (UnigineScript)
#ifdef _ANDROID
	#include <my_project/scripts/android_controls.h>
#endif

#ifdef HAS_APP_TABLET && _ANDROID		
		// Run a Java function
		engine.tablet.call("com/company/project/ClassName","doSomething");
		
		// Up to 2 arguments are supported (int, float and string types)
		// Arguments should be of the same type
		engine.tablet.call("com/unigine/callbacks/UnigineCallbacks","runSystem","Hello","From Java");
		engine.tablet.call("com/unigine/callbacks/UnigineCallbacks","runSystem",2.0f,4.0f);
#endif 

Code for iOS

To code iOS-specific logic, you can use the _IOS define:

Source code (UnigineScript)
#ifdef _IOS
	#include <my_project/scripts/ios_controls.h>
#endif

#ifdef HAS_APP_TABLET && _IOS
	int width = engine.tablet.getWidth();
	int height = engine.tablet.getHeight();
#endif 

It is also possible to use _IPAD, _IPHONE and _IPOD defines with versions to target specific devices.

Notice
iPod devices are detected by the system with _IPOD and _IPHONE defines at the same time. Because of that, iPod-specific code should always go before iPhone-specific one.
Source code (UnigineScript)
#ifdef HAS_APP_TABLET && _IOS
			
	// Set a video mode
	int width = engine.tablet.getWidth();
	int height = engine.tablet.getHeight();
	
	// For iPad 3 and higher
	#if defined(_IPAD) && (_IPAD >= 3)
		engine.console.setInt("video_mode",-1);
		engine.console.setInt("video_width",width / 2);
		engine.console.setInt("video_height",height / 2);
		engine.console.run("video_restart");
	// For iPhone 5 and higher
	#elif defined(_IPHONE) && (_IPHONE >= 5)
		engine.console.setInt("video_mode",-1);
		engine.console.setInt("video_width",width);
		engine.console.setInt("video_height",height);
		engine.console.run("video_restart");
	#endif
	
	engine.console.setInt("render_force_no_shadows",1);
	
#endif

Optimize Your Content

Optimize your exported models and objects used in game:

  • It is important to use models with low polygon count. Avoid using models with excessive number of faces.
  • Smooth your models and keep the number of UV mapping seams low. For a graphics card, vertices that have multiple normals (as hard edges do) or multiple UV coordinates cannot be shared for polygon faces. Instead, such vertices are defined multiple times in the array of vertices. That means that the actual number of vertices that graphics hardware will have to process will be bigger than geometric vertex count of your model.
  • Mobile devices have relatively low fillrate. If a mesh has multiple layers, the amount of overdraw can drastically decrease performance on some devices (for example, NVIDIA Tegra 2).
  • Use skinned animation with as few bones as possible.
  • Right now the terrain (together with its material) is not supported on mobile devices. Instead, you can use a simple mesh that has the same looking material. Keep in mind that this mesh should be extremely low-poly one.
  • Water is also currently not supported.
  • Avoid scaling your meshes; otherwise you will have to enable lighting normalization (see details below).

Optimize your materials:

  • Always downscale and compress your textures (see details below). 512x512 texture is already very large for mobile devices!
  • If non-compressed format is still necessary (for example, for UI) prefer 16-bit textures over 32-bit ones. At least memory bandwidth will be reduced by half in this case.
  • Keep the number of used materials low and share textures whenever possible.
  • Avoid or minimize the usage of transparent textures.
  • For iOS it is strongly recommended not to use alpha-testing as it is very expensive. Use alpha blending instead.
  • Bake all reflections, because dynamic reflections and optimal performance on mobile devices hardly go together.

Texture Compression

Compressing textures is essential and significantly improves performance because it does the following:

  • saves memory footprint
  • reduces memory bandwidth usage
  • increases texture cacheability

Compression on a target device is CPU intensive and because of this all images should be pre-compressed. There is no format that is uniformly supported on all mobile devices. Depending on the targeted device, the natively supported compression formats are as follows.

Device Compressed Image Formats
NVIDIA Tegra 2 ETC1*, DXT1
iPhone / iPad PVR4**
Android devices ETC1*
Notice
*ETC1 does not support the alpha channel, so it can only be used for opaque textures.
**PVR4 supports transparency. But only those textures can be compressed into this format that are square and at least 8x8 pixels in size.

Textures can be compressed using ImageDDS tool. There are special options for batch compression for both Android and iOS platforms. They are packaged into *.dds container.

Bake lighting:

  • Use lightmapped environments.
  • Avoid using dynamic lighting. Depending on the target device, even one or two dynamic lights can be enough to drop the framerate. Instead, set up all dynamic lights and bake them into light maps using Lightmap generation tool.
  • There is no hardware support for shadows on modern mobile devices. Objects will not cast shadows from dynamic lights in any way.

Optimizing physics:

  • Heavy physics can be a performance killer for mobile devices, as it requires a lot of floating-point computing. The total amount of physics calculations depends on the number of non-frozen bodies and shapes in the scene. However, the application framerate on the targeted device can be high enough to freely use physics simulation due to NEON instruction set support (available on iOS only). In other cases, it is recommended to create as few bodies as possible or not to use physics at all.
  • If you need to use physics in your scene, choose rigid bodies as they are the cheapest.
  • Do not use Convex as collision shapes as they require the most intensive calculations. Use simple primitives instead.

Optimize Scene Rendering

Besides content optimization, you also need to simplify the overall rendering process in order to provide the efficient application performance. This can be done by lowering the quality of shaders and skipping unnecessary rendering passes.

Use Simple Shaders

Unigine has a simplified set of shaders aimed specifically for optimized rendering on mobile devices. These simple shaders are fully compatible with the default (and heavy) ones designed for desktop computers.
Simplified shaders are automatically used by the engine when the application is run on a mobile device. To enable usage of simplified shaders when developing on a desktop machine, you need to set an extern define RENDER_SIMPLE (available only in OpenGL) in one of the following ways:

  • Set this define in the system script unigine.cpp. You cannot set it in other scripts, because shaders should be loaded at the very start.
    Source code (UnigineScript)
    #define RENDER_SIMPLE
    
  • Pass an extern define RENDER_SIMPLE as a CLI argument:
    Shell commands
    main_x86.exe -extern_define RENDER_SIMPLE
    
  • Set it via the console: extern_define RENDER_SIMPLE. In this case, you need to exit your application and launch it again.
Notice
If the RENDER_SIMPLE define is set as a CLI argument or via the console, it will be automatically stored in the configuration file and used when you run your Unigine-based application next time.
To go back to default shaders, simply set extern_define "".

Here is how the default and the simplified shaders look like.

Default shaders
Default shaders
  Simple shaders
Simplified shaders

Decrease Shaders Quality

Go to Main menu -> Render tab and decrease the quality of shaders:

  • The low quality provides the best performance on mobile devices; however, the rendering quality decreases. This option is recommended for various Android and iOS devices.
  • If FPS on the target device is high enough, you can set the medium quality of shaders. This setting provides more realistic lighting. This option can be set for Tegra 2.

Skip Rendering Options

Some rendering options are explicitly not supported by simplified shaders. Foe example, ambient occlusion and HDR rendering are too expensive to be rendered on mobile devices. Other rendering passes need to be disabled manually depending on the application content.

To skip a rendering pass, you need to go to Tools -> Render tab and choose one of Skip options. You can also disabled passes via the console by typing render_skip_* 1.

Deferred pass Skip rendering into the deferred buffer (Skip deferred option). As no depth information will be available in this case, the following passes will be automatically disabled.

You can use deferred buffer together with these effects on Tegra 2; however, it dramatically decreases performance.
Opacity ambient pass Ambient light pass for opaque objects will light objects with baked light maps:
  • With low shader quality, normal maps are not used.
  • With medium shader quality, normal maps are used.
Opacity light passes If there are no dynamic lights in the scene (recommended), set Skip opacity light option.
If any are present, keep lights passes for opaque objects enabled. Take notice that each light will rendered in a separate pass, which means the higher number of dynamic lights, the more decreased performance. No shadows are rendered due to the lack of hardware-level support.
Transparent ambient pass If there are no transparent materials, set Skip transparent ambient option.
If any are present (for example, transparent particles), ambient light pass for opaque objects needs to be rendered.
Transparent light pass Unless both transparent materials and dynamic lights are present in the scene, skip light passes for transparent objects with Skip transparent light option. Using transparency together with dynamic lighting is strongly not recommended, as it increases the number of passes. Again, no shadows will be rendered.
Decal Ambient pass Unless you use decals, skip these passes with Skip decal ambient options.
Decal Light passes Unless you use both decals and dynamic lighting, skip these passes with Skip decal light options.
Reflection Skip rendering dynamic reflections with Skip reflection option. Use static reflections instead.
Post Materials If there are no postprocesses in the scene, such as DOF, glow, refraction, etc. set Skip post materials option (recommended).
You can apply postprocesses (default or custom ones), but keep in mind that they significantly decrease performance. They are rendered in the following way:
  1. The screen is copied into a separate buffer.
  2. To this buffer the postprocess is applied.
  3. And only after that the image is rendered onto the screen.
Postprocesses can eat up 5-10 FPS if rendered. In contrast, all other passes are rendered directly onto the screen.
Environment light modulation Skip color correction according to the environment texture. For that, you need to go to Tools -> Render tab and uncheck Use environment.
Lighting normalization If there are no scaled objects (recommended), skip tangent basis normalization: go to Tools -> Render tab and uncheck Use normalization.
Last update: 2017-07-03