Google is committed to advancing racial equity for Black communities. See how.

Overview of Android Performance Tuner (native)

This topic describes how to integrate Android Performance Tuner, also known as the Tuning Fork library, to record and upload frame time data from native (C and C++) game engines.

For the Unity game engine, see the guide for Unity.

Background

A key component of the game experience is rendering performance. Rendering performance is an outcome of the following two inputs:

  • Frame rate: How often a frame is drawn.
  • Graphical quality settings: Level of fidelity at which a frame is presented, including simulation fidelity as well as graphics.

For games, good rendering performance is defined as the following:

  • Delivering a stable, consistent frame rate (that is, the percentage of frames rendering at the desired frequency).
  • Rendering frames at the highest frequency possible while maintaining stability, typically 30 or 60 FPS depending on the type of game.
  • Maximizing the level of detail for a user given their screen size and density while still achieving a desired, stable frame rate.

The Android Frame Pacing library limits much of the variation in frame times, providing a stable frame rate for games. The remaining variation in frame times is due to the level of detail displayed during certain scenes in gameplay and the graphical capabilities of the device. Using Android Performance Tuner, you can pinpoint times during gameplay when the frame time is slower or faster than your target, and correlate these issues and opportunities to:

  • Specific quality settings
  • Specific scenes in your game
  • Specific device models or device specs

Record and upload data

The Tuning Fork library relies on one of its tick functions being called each frame by the Android Frame Pacing library. Within the library, this tick information is aggregated into histograms which are then periodically uploaded to Google Play through an HTTP endpoint. Each tick is recorded as being associated with an instrument key and an annotation, the definitions for which you specify in a protocol buffer file.

Instrument keys

An instrument key indicates where in the frame the tick comes from and is an integer that must be passed to each tick function call. The Android Frame Pacing library uses a predefined set of instrument keys, defined in swappy_common.h. You can also define your own instrument keys if you do not use the Frame Pacing library.

Annotations

Annotations give contextual information about what your game is doing when a tick is recorded. For example, an annotation could identify any of the following:

  • The current game level
  • A specific scene is loading
  • A "big boss" is on the screen
  • Any other relevant game state information

Annotations are defined by the com.google.tuningfork.Annotation protocol buffer message. To set the current annotation, you pass a serialization of the message you defined to TuningFork_setCurrentAnnotation(). All subsequent tick data is then associated with this annotation until another annotation is set. The following is an example proto definition for annotations:

import "tuningfork.proto"
enum LoadingState {
  INVALID_LS = 0;
  LOADING = 1;
  NOT_LOADING = 2;
}
enum Level {
  INVALID_LEVEL = 0;
  Level_1 = 1;
  Level_2 = 2;
  Level_3 = 3;
}
message Annotation {
  optional LoadingState loading_state = 1;
  optional Level level = 2;
}

Fidelity parameters

Fidelity parameters influence the performance and graphical fidelity of your game, such as mesh level-of-detail, texture resolution, and anti-aliasing method. Like annotations, fidelity parameters are defined using the com.google.tuningfork.FidelityParams protocol buffer message. The following is an example proto definition for fidelity parameters:

import "tuningfork.proto"
message FidelityParams {
  int32 texture_quality_level = 1;
  int32 shadow_resolution = 2;
  float terrain_details_percent = 3;
  int32 post_processing_effects_level = 4;
}

At Tuning Fork initialization, you pass a serialization of the parameters that the game uses. You can change these parameters if, for example, the user changes the game rendering settings, and your subsequent uploaded data is associated with the new parameters.

In order for Google Play to understand the annotations and fidelity parameters that you define, the protocol buffer file holding these definitions must be bundled within the game’s APK, together with initialization settings. You also need to provide default values for common fidelity parameter combinations in your APK in order to have your data segmented by them in the Google Play UI. For more information, see Define quality levels.

Memory and CPU overhead

All memory used by the Tuning Fork library is allocated at initialization in order to avoid surprises during gameplay. The size of the data depends on the number of instrument keys, number of possible annotations, and number of buckets in each histogram; it is a multiple of all of these times four bytes for each bucket. There are also two copies of all histograms to allow for submission in a double-buffered fashion.

Submission occurs on a separate thread and doesn't block tick calls. If no upload connection is available, the submission is queued for later upload.

There is little processing overhead to calling a tick function: it simply calculates an index into the array of histogram buckets and increments an integer count.

Integrate the Tuning Fork library

This integration guide is divided into two parts. The first part describes how to run an end-to-end test using a demo app and the Google Play Console. The second part describes how to integrate the Tuning Fork library into your toolchain and how to use the functions that the library provides. Click the Next link below to get started.