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

Define annotations, fidelity parameters, and settings

This document describes how to set annotations, fidelity parameters, and settings in your project.

Annotations and fidelity parameters

Annotations give contextual information about what your game is doing when a tick is recorded. Fidelity parameters reflect the performance and graphical settings of your game. You define these using protocol buffers, which are Google’s language-neutral, structured, data-interchange format. For more information on using protocol buffers within your game, see About protocol buffers.

The possible annotations and fidelity parameters for your game are defined in a file called dev_tuningfork.proto, which is located in the assets/tuningfork directory of your project. The following is an example from the demo app:

syntax = "proto3";


enum InstrumentKey {
  CPU = 0;
  GPU = 1;

enum LoadingState {
  LOADING = 2;

enum Level {
  // 0 is not a valid value
  LEVEL_1 = 1;
  LEVEL_2 = 2;
  LEVEL_3 = 3;

message Annotation {
  LoadingState loading = 1;
  Level level = 2;

message FidelityParams {
  int32 num_spheres = 1;
  float tesselation_percent = 2;

Note the following:

  • The package must be
  • The message names must be exactly Annotation and FidelityParams.
  • You can use only enums defined in this file as part of annotations.
  • You can only use enums, int32s or floats in FidelityParams fields.
  • The validation tool enforces these conventions.


The Settings message is defined by tuningfork.proto. See a full example in the following file:


You must define the settings for your game in a file called tuningfork_settings.txt located in the assets/tuningfork directory of your project. You need to specify only the following fields:

  • aggregation_strategy: A message containing the following:

    • method: TIME_BASED to upload every n milliseconds or TICK_BASED to upload every n ticks.
    • intervalms_or_count: n for the method field.
    • max_instrumentation_keys: Number of instrumentation keys to use. Set to 4 if using the Android Frame Pacing library.
    • annotation_enum_size: An optional field since the size is calculated at start-up from the descriptor.
  • api_key: Your app's Cloud project API key, used to validate requests to the endpoint. To generate this key, see Enable the API. If you see connection errors in logcat, check that the API key is correct.

  • default_fidelity_parameters_filename: The fidelity parameter set used at initialization (optional if you set the training_fidelity_params in your code).

  • loading_annotation_index: (Optional) The index in your annotation fields of the LoadingState flag.

  • level_annotation_index: (Optional) The index in your annotation fields of the level number.

The following is an example text representation:

aggregation_strategy: {method: TIME_BASED, intervalms_or_count: 10000,
  max_instrumentation_keys: 5, annotation_enum_size: [3,4]}
default_fidelity_parameters_filename: "dev_tuningfork_fidelityparams_3.bin"
loading_annotation_index: 1
level_annotation_index: 2

Loading annotations

Loading annotations mark the frames that are part of the level-loading process. You should use loading annotations so that slower frames that occur while the game is loading do not affect your overall metrics.

When you set the loading_annotation_index field in your settings, a special meaning is given to the annotation with that index. For example, in the demo app, the following annotation is used:

message Annotation {
  LoadingState loading = 1;
  Level level = 2;

While the loading annotation is set to LOADING, frame ticks are ignored. When the loading annotation is reset to NOT_LOADING, the time spent in the LOADING state is recorded in the histogram for that annotation. Use this annotation to track how long it takes to load a level in your game. You specify the level number as a value from the Level enum.

In the above settings, since loading_annotation_index is 1, the first annotation specifies whether the game is loading the next scene or not. Since level_annotation_index is 2, the second annotation specifies the level number.

You need to manually set these annotations during your game. You can see an example of this in the demo app as it cycles through all of the game levels automatically. For more information, see the SetAnnotations() function in insightsdemo.cpp.

Define quality levels

Use quality levels to annotate sessions so that you can determine if devices are running on a quality level that is too high (resulting in lower performance) or too low (resulting in unnecessarily reduced fidelity).

You must define at least one, and preferably several, quality levels for your game. A quality level corresponds to an instance of your FidelityParams message. These levels must be given in increasing fidelity order with the following filename format:


where i is an index starting at 1 with a maximum value of 15. These files must be located in the assets/tuningfork directory of your project. The sample project shows an example of this structure in the gamesdk/samples/tuningfork/insightsdemo/app/src/main/assets/tuningfork/ directory.

About protocol buffers

The Tuning Fork library uses Google’s protocol buffer format for settings, annotations, and fidelity parameters. This is a well-defined, multi-language protocol for extensible, structured data. For more information, see the Protocol Buffers documentation.

Proto2 vs proto3

The version of the protocol buffer format is set in the first line of the file:


Proto2 and proto3 are two commonly-used versions of protocol buffers. They both use the same wire format but the definition files are not compatible. Key differences between the two versions include the following:

  • The optional and required keywords are no longer allowed in proto3.
  • Everything is effectively optional in proto3.
  • Extensions are not supported in proto3.

Use proto3 in your proto files since these can be compiled to C#. Proto2 works as well with the limited feature set used in the Tuning Fork library.

Text versus binary representations

The binary protobuf wire-format is well-defined and stable across different protobuf versions (the generated code is not). There is also a text format that the full version of the protobuf library can generate and read. This format is not as well-defined, but it is stable for the limited set of features in the Tuning Fork library. You can convert between binary and text formats using the protoc compiler. The following command converts a text protobuf to binary:

protoc --encode tuningfork.proto < tuningfork_settings.txt > tuningfork_settings.bin

You must include binary files rather than text files in your APK because the full protobuf library is several MB in size; making the Tuning Fork library depend on it would increase the size of your game by a similar amount.

Full versus Lite versus Nano

As well as the full protobuf library, there is a lite version that reduces code footprint by removing some features such as reflection, FileDescriptors, and streaming to and from text formats. This version still requires several MB of extra code footprint and so, the Tuning Fork library internally uses the nanopb library. The source code for this library is included in the Android Open Source Project in external/nanopb-c and it is part of the gamesdk branch. Use this library in your game if code size is an issue.

There are CMake files in gamesdk/src/protobuf that can help you to integrate all three versions of protobuf. The samples use a mix of both nanopb and full protobuf.