Cómo agregar funciones de registro de los tiempos de carga

Es importante realizar la grabación cuando tu juego realiza eventos de carga por dos motivos:

  1. Para evitar contaminar los datos de la latencia de fotogramas durante la carga
  2. Para analizar los tiempos de carga a fin de verificar cuándo y dónde son más largos de lo aceptable

Un evento de carga puede tener metadatos asociados:

public class LoadingTimeMetadata
{
    public enum LoadingState
    {
        Unknown = 0,

        /// <summary>
        ///     The first time the game is run.
        /// </summary>
        FirstRun = 1,

        /// <summary>
        ///     App is not backgrounded.
        /// </summary>
        ColdStart = 2,

        /// <summary>
        ///     App is backgrounded.
        /// </summary>
        WarmStart = 3,

        /// <summary>
        ///     App is backgrounded, least work needed.
        /// </summary>
        HotStart = 4,

        /// <summary>
        ///     Asset loading between levels.
        /// </summary>
        InterLevel = 5
    }

    public LoadingState state;

    public enum LoadingSource
    {
        UnknownSource = 0,

        /// <summary>
        ///     Uncompressing data.
        /// </summary>
        Memory = 1,

        /// <summary>
        ///     Reading assets from APK bundle.
        /// </summary>
        Apk = 2,

        /// <summary>
        ///     Reading assets from device storage.
        /// </summary>
        DeviceStorage = 3,

        /// <summary>
        ///     Reading assets from external storage, e.g. SD card.
        /// </summary>
        ExternalStorage = 4,

        /// <summary>
        ///     Loading assets from the network.
        /// </summary>
        Network = 5,

        /// <summary>
        ///     Shader compilation.
        /// </summary>
        ShaderCompilation = 6,

        /// <summary>
        ///     Time spent between process starting and onCreate.
        /// </summary>
        PreActivity = 7,

        /// <summary>
        ///     Total time spent between process starting and first render frame.
        /// </summary>
        FirstTouchToFirstFrame = 8,

        /// <summary>
        ///     Time from start to end of a group of events.
        /// </summary>
        TotalUserWaitForGroup = 9
    }

    public LoadingSource source;

    /// <summary>
    ///     0 = no compression, 100 = max compression
    /// </summary>
    public int compression_level;

    public enum NetworkConnectivity
    {
        Unknown = 0,
        Wifi = 1,
        CellularNetwork = 2
    }

    public NetworkConnectivity network_connectivity;

    /// <summary>
    ///     Bandwidth in bits per second.
    /// </summary>
    public ulong network_transfer_speed_bps;

    /// <summary>
    ///     Latency in nanoseconds.
    /// </summary>
    public ulong network_latency_ns;
}

Los campos que no sean relevantes para tus necesidades pueden ser cero.

Un evento de carga también puede tener una anotación asociada. Puedes definir de la misma manera que las anotaciones de la latencia de fotogramas, mediante uno o más campos en el mensaje Annotation.

Result<ulong> StartRecordingLoadingTime(LoadingTimeMetadata eventMetadata, TAnnotation annotation);

Esta función comienza a grabar un evento de tiempo de carga asociado con la anotación y los metadatos determinados, y completa un elemento Result<ulong>.value que se usará en la función StopRecordingLoadingTime().

ErrorCode StopRecordingLoadingTime(ulong handle);

Esta función deja de grabar un evento que StartRecordingLoadingTime() inició previamente. El evento se carga en la próxima limpieza de la sesión.

Funciones de grupos de carga

En tu juego, puedes grabar varios eventos de carga durante un período único de carga que ve el usuario. Algunos ejemplos son la carga de archivos, la carga de escena, la descompresión y compilación de sombreadores.

Es importante informar a Android Performance Tuner que los eventos de carga son parte de ese grupo para que pueda brindar mejores estadísticas. Para ello, agrupa tus eventos de carga con las siguientes funciones de inicio y finalización.

Result<ulong> StartLoadingGroup(LoadingTimeMetadata eventMetadata, TAnnotation annotation);

Esta función inicia un grupo de carga asociado con la anotación y los metadatos determinados, y completa un elemento Result<ulong>.value que se usará en la función StopLoadingGroup(). Por el momento, el backend de Play no usa los metadatos ni la anotación. Sin embargo, la anotación se puede configurar en null. Todos los eventos de carga posteriores se etiquetan con un ID de grupo único.

ErrorCode StopLoadingGroup(ulong handle);

Esta función detiene un grupo de carga que StartLoadingGroup() inició previamente. Los eventos de carga posteriores no tendrán un ID de grupo hasta que se vuelva a llamar a StartLoadingGroup().

Figura 1: Ejemplo del grupo de carga.

Ejemplos

Estos son algunos ejemplos de cómo agregar funciones de tiempo de carga a tu juego.

Eventos de carga de archivos

En el siguiente ejemplo de código, se muestra cómo grabar eventos de carga de archivos en tu juego.

public RawImage image;

IEnumerator LoadImageFromStreamingAssets(string imageName)
{
    string imagePath = "file://" + Path.Combine(Application.streamingAssetsPath, imageName);
    using (var r = UnityWebRequestTexture.GetTexture(imagePath))
    {
        LoadingTimeMetadata fileLoadingMetadata = new LoadingTimeMetadata()
        {
            state = LoadingTimeMetadata.LoadingState.InterLevel,
            source = LoadingTimeMetadata.LoadingSource.DeviceStorage,
            // Fields are zero by default but they could be set as follows
            compression_level = 0,
            network_connectivity = 0,
            network_transfer_speed_bps = 0,
            network_latency_ns = 0
        };
        Annotation annotation = new Annotation()
        {
            Scene = Scene.MagicalForest
        };
        // Start recording loading time.
        Result<ulong> result = performanceTuner.StartRecordingLoadingTime(fileLoadingMetadata, annotation);
        yield return r.SendWebRequest();
        // Stop recording loading time.
        performanceTuner.StopRecordingLoadingTime(result.value);
        if (r.isNetworkError || r.isHttpError)
        {
            Debug.Log(r.error);
        }
        else
        {
            Texture2D tex = DownloadHandlerTexture.GetContent(r);
            image.texture = tex;
        }
    }
}

Eventos de carga de escenas

En el siguiente ejemplo de código, se muestra cómo grabar eventos de carga de escenas en tu juego.

IEnumerator LoadScene(int sceneIndex)
{
    LoadingTimeMetadata metadata = new LoadingTimeMetadata()
        {state = LoadingTimeMetadata.LoadingState.InterLevel};
    Annotation annotation = new Annotation() {Scene = (Scene) (sceneIndex + 1)};
    Result<ulong> result = performanceTuner.StartRecordingLoadingTime(metadata, annotation);
    AsyncOperation asyncSceneLoad = SceneManager.LoadSceneAsync(sceneIndex, LoadSceneMode.Single);
    while (!asyncSceneLoad.isDone)
    {
        yield return null;
    }

    performanceTuner.StopRecordingLoadingTime(result.value);
}

Funciones de grupos de carga

En el siguiente ejemplo de código, se muestra cómo agregar funciones de grupos de carga a tu juego.

IEnumerator LoadImages()
{
    LoadingTimeMetadata groupMetadata = new LoadingTimeMetadata()
    {
        state = LoadingTimeMetadata.LoadingState.InterLevel,
        source = LoadingTimeMetadata.LoadingSource.DeviceStorage,
    };
    Result<ulong> result = performanceTuner.StartLoadingGroup(groupMetadata, null);
    yield return StartCoroutine(LoadImageFromStreamingAssets("image1.jpeg"));
    yield return StartCoroutine(LoadImageFromStreamingAssets("image2.jpeg"));
    yield return StartCoroutine(LoadImageFromStreamingAssets("image3.jpeg"));
    yield return StartCoroutine(LoadImageFromStreamingAssets("image4.jpeg"));
    var stopErrorCode = performanceTuner.StopLoadingGroup(0);
}