Version: 2020.1
XR SDK PreInit 接口
开源代码仓库

XR SDK Stats 接口

XR SDK Stats 接口用于注册和管理统计数据。

概述

使用 XR Stats 接口记录各个子系统之间的统计数据。浮点数是唯一支持的统计数据原始类型。

使用 UnityPluginLoad 入口点方法获取指向 XRStats 接口的指针:

IUnityXRStats* sXRStats = nullptr;

extern "C" void UNITY_INTERFACE_EXPORT UnityPluginLoad(IUnityInterfaces* unityInterfaces)
{
    sXRStats = (IUnityXRStats*)unityInterfaces->GetInterface(UNITY_GET_INTERFACE_GUID(IUnityXRStats));
    //...
}

用法

使用 stats 接口注册您的子系统和各个 stat 定义:

static UnityXRStatId m_GPUFrameTimeID;
static UnityXRStatId m_DroppedFrameCountID;
static UnityXRStatId m_WorkThreadStat;

static UnitySubsystemErrorCode ExampleDisplayProvider_Start(UnitySubsystemHandle handle)
{
    if (sXRStats)
    {
        sXRStats->RegisterStatSource(handle);
        m_GPUFrameTimeID = sXRStats->RegisterStatDefinition(handle, "Example.GPUTime", kUnityXRStatOptionNone);
        m_DroppedFrameCountID = sXRStats->RegisterStatDefinition(handle, "Example.DroppedFrame", kUnityXRStatOptionNone);
        m_WorkThreadStat = sXRStats->RegisterStatDefinition(handle, "Example.WorkerThreadStat", kUnityXRStatOptionNone);
    }

    return kUnitySubsystemErrorCodeSuccess;
}

更新 Gfx 线程上的统计数据:

extern float GetLastGPUTime(); //由您的运行时提供
static void ExampleDisplayProvider_GfxThreadCall(UnitySubsystemHandle handle)
{
    sXRStats->SetStatFloat(m_GPUFrameTimeID, GetLastGPUTime());
    // 执行 gfx 线程的操作
}

更新主线程上的统计数据:

extern float GetDroppedFrameCount(); //由您的运行时提供

static void ExampleDisplayProvider_MainThreadCall(UnitySubsystemHandle handle)
{
    sXRStats->SetStatFloat(m_DroppedFrameCountID, GetDroppedFrameCount());
    // 执行主线程的操作
}

更新您自己的线程上统计数据,但务必调用 IncrementStatFrame 以保持该线程的当前帧与其他线程同步(内部对主线程和图形线程进行管理):

extern float GetWorkerThreadStat(); //由您的运行时提供
static void ExampleDisplayProvider_MyWorkerThread(UnitySubsystemHandle handle)
{
    sXRStats->IncrementStatFrame();
    sXRStats->SetStatFloat(m_WorkThreadStat, GetWorkerThreadStat());
    // 执行工作线程的操作
}

当子系统停止时取消注册统计源:

static void ExampleDisplayProvider_Stop(UnitySubsystemHandle handle)
{
    sXRStats->UnregisterStatSource(handle);
}

线程安全

通过 SetStatFloat 更新统计数据是线程安全的。但是,注册和取消注册统计数据源不是线程安全的,只能在统计数据源生命周期的 Start 和 Stop 函数期间在主线程上完成。

限制

处理统计数据的队列大小为 2000。该队列在所有线程和所有子系统之间共享,并且仅在帧完成时提供服务。为此,您应该将调用 SetStatFloat 的次数保持在较低的水平,以避免填满队列。

注意:队列已满时记录的任何统计数据都将丢失。

向用户公开统计数据

UnityEngine.XR.Provider 命名空间中,使用 public static bool TryGetStat(Experimental.IntegratedSubsystem xrSubsystem, string tag, out float value) 获取向您的提供程序注册和更新的统计数据:

using UnityEngine.XR.Provider;
using System.Collections.Generic;
using UnityEngine.Experimental.XR;
using UnityEngine.Experimental;
using UnityEngine;

public static class ExampleProviderStats
{
    public static float GPUFrameTime()
    {
        float tmp;
        XRStats.TryGetStat(GetFirstDisplaySubsystem(), "Example.GPUTime", out tmp);
        return tmp;
    }

    public static int DroppedFrameCount()
    {
        float tmp;
        XRStats.TryGetStat(GetFirstDisplaySubsystem(), "Example.DroppedFrame", out tmp);
        return (int)tmp;
    }

    public static float MyWorkerThreadStat()
    {
        float tmp;
        XRStats.TryGetStat(GetFirstDisplaySubsystem(), "Example.WorkerThreadStat", out tmp);
        return tmp;
    }

    // etc...
    private static IntegratedSubsystem GetFirstDisplaySubsystem()
    {
        List<XRDisplaySubsystem> displays = new List<XRDisplaySubsystem>();
        SubsystemManager.GetInstances(displays);
        if (displays.Count == 0)
        {
            Debug.Log("No display subsystem found.");
            return null;
        }
        return displays[0];
    }
}

编写公共访问器方法(如上面的示例所示)可以帮助用户获取统计数据,而无需筛查提供程序文档并查找注册了统计数据的子系统。

此外,一些子系统具有预定义的统计数据标签。您的提供程序可以为预定义的统计 API 提供统计数据,Unity 可通过注册子系统特定的统计数据标签进行公开(例如:Headers/XR/UnityXRDisplayStats.h)。

XR SDK PreInit 接口
开源代码仓库