管理电源和热

当 HoloLens 2 在温暖的环境中运行或具有很高的性能要求(CPU/GPU 使用、外围设备使用等)时,它可能会变得非常热,以至于会自动采取措施来防止自身处于过热状态。 这些措施包括以下内容:

  • 调整充电性能
  • 提供用户反馈
  • 关闭应用程序

...在最坏情况下:

  • 关闭 HoloLens 2

如果应用程序需要较高的外设性能,请考虑使用 PowerThermalNotification 软件开发工具包 (SDK) 来订阅通知事件并实现自己的自定义操作。 这样做可以让设备运行更长时间,否则系统可能会终止应用程序运行。

注意

22H1 版本中包括对 Microsoft.MixedReality.PowerThermalNotification SDK 的支持。

本文介绍 PowerThermalNotification SDK 及其用于入门的基本用法。

在哪里获取 SDK?

可以通过混合现实功能工具下载 PowerThermalNotification SDK。

PowerThermalNotification SDK 支持 C# 和 C++ 的语言投影,使开发人员能够开发适用于 Win32 或 UWP 平台的应用程序。

概念概述

HoloLens 2 消耗的电量会以热量形式散发出来。 传统电脑设备会使用风扇来解决散热问题,但可穿戴设备必须是轻型设备。 因此,散热解决方案更为复杂。 HoloLens 2 具有内置硬件和软件安全功能,可确保用户不会感觉头戴显示设备过热,不过这些功能还必须与用户体验进行平衡。 例如,如果我们知道 HoloLens 2 的哪个部分正在升温,我们就可以选择对发热的外围设备进行管控。 作为最后的手段,我们可能会关闭导致发热的应用程序。

HoloLens 2 通过使用温度传感器来处理发热问题。 热框架将传感器组与设备上不同的外围设备进行连接。 传感器已分组,因为可能无法确定物理区域中哪个外围设备引起 HoloLens 2 发热。

PowerThermalNotification SDK 公开监视这些传感器组所需的 API。 当应用程序使用的外围设备显示出可能需要缓解措施的迹象时,将触发 SDK 事件。 然后,应用程序可以调整其客户体验,以减少发热影响。 降低影响意味着降低系统操作(如应用程序或设备关闭)的风险。

使用 CPU 处理大量视频数据的应用程序就是一个简单的示例。 该应用程序可以订阅 CPU 组件的性能通知。 当应用程序收到通知时,它可以减少 CPU 的工作负载。 如果收到另一个事件表明不需要进一步的缓解措施,则可以还原 CPU 工作负载。

平台响应

下表按外围设备对系统操作进行细分。 可以使用 SDK 取消下述操作。 请参阅取消默认系统缓解

外围设备 MinimumUserImpact MediumUserImpact MaximumUserImpact 最后手段 软件关闭 防故障
GPU 限制 MRC 质量
调整 VSYNC 间隔
显示 深度 FPS 缩减
任何外围设备 显示警告
关闭应用程序
停止 MRC 捕获
OS 关闭 硬件关闭

注意

无法取消“最后手段”、“软件关闭”和“防故障”列中的操作。

应用程序响应建议

以下是应用程序可以根据需要缓解的外围设备采取的建议缓解措施的细分。 由于每个应用程序都不同,因此由应用程序开发人员决定哪些操作可能对每个外围设备产生更显着的影响。 开发人员应该根据对最终用户的影响确定所采取操作的优先级。

外围设备的建议缓解措施

CPU

GPU

DRAM

网络

Battery

显示

  • 增加场景中黑色像素的数量
  • 使用低功耗颜色(例如绿色)
  • 让显示器变暗

照相机/摄像机

  • 概述
  • 降低相机分辨率
  • 降低相机帧速率
  • 减少相机图像的应用后处理
  • 停止使用照相机/摄像机

实现用例

SDK 旨在支持两种标准用例以获取信息:

  • 基于事件
  • 基于轮询

基于事件的通知将为需要采取措施的应用程序提供最快的反馈路径。 但是,在某些情况下,开发人员使用轮询可能更方便。

注意

每个外围设备的状态信息最多每几秒钟更新一次,因此比这更快的轮询可能会浪费 CPU 周期。

基于事件的 API 使用情况

注册事件

若要获取通知,有三个要求:

如果应用程序不满足这些要求,将无法收到事件。

可以使用 IsSupported 函数检查第一项。 如果系统支持掩码中至少一个外围设备的通知,则该函数将返回 true。 只要应用程序不显式依赖于 PowerThermalNotification SDK 事件,就可以选择不使用此函数检查支持。

满足上述三项要求后,你将收到所有受支持的 PeripheralsOfInterest 的初始通知。 如果稍后更改 PeripheralsOfInterest 或任一事件处理程序,你将收到另一组基于当前状态的通知。

以下代码片段用于抓取 PowerThermalNotification 类实例并将其配置为用于 PowerThermalPeripheralFlags.CpuPowerThermalPeripheralFlags.PhotoVideoCamera 的通知:

using Microsoft.MixedReality.PowerThermalNotification;

private void NotificationHandler(object sender, PowerThermalEventArgs args)
{
    //  Notification handling can be done here using information contained in args
}

private void InitializeThermalNotifications()
{
    PowerThermalNotification p = PowerThermalNotification.GetForCurrentProcess();
    
    PowerThermalPeripheralFlags requestedFlags = PowerThermalPeripheralFlags.Cpu | PowerThermalPeripheralFlags.PhotoVideoCamera;
     if (PowerThermalNotification.IsSupported(requestedFlags))
    {
        //At least one of these peripherals is supported by the system
        p.PeripheralsOfInterest = requestedFlags;
        p.PowerThermalMitigationLevelChanged += NotificationHandler;
    }  
}

处理 事件

PowerThermalMitigationLevelChanged 事件触发时,还会跟着触发 PowerThermalEventArgs。 它们应用于了解事件。

同样地,当 PowerThermalThermalScoreChanged 事件触发时,还会跟着触发 PowerThermalScoreArgs

收到事件后,事件处理程序应检查 args.ImpactedPeripherals,它用于标识哪些外围设备受到影响(可能不止一个)。

对于 PowerThermalMitigationLevelChanged 事件,args.MitigationLevel 指示建议对指定外围设备采取什么级别的缓解措施。 如果 args.MitigationLevelPowerThermalMitigationLevel.NoUserImpact,则应删除与指定外围设备相关的任何缓解措施。

对于 PowerThermalThermalScoreChanged 事件,args.ThermalScore 指示从 100 到 0 的分数,从而反映接近应用程序关闭事件(零)的线性比例。 热评分范围从缓解报告范围之外开始,以便在接近缓解要求时,提前通知应用程序。

下面是一个示例处理程序:

bool doCpuThrottle = false;

private void NotificationHandler(object sender, PowerThermalEventArgs args)
{
    if (args.ImpactedPeripherals.HasFlag(PowerThermalPeripheralFlags.Cpu))
    {
        if(args.MitigationLevel = PowerThermalMitigationLevel.NoUserImpact)
        {
            doCpuThrottle = false;
        }
        else if(args.MitigationLevel >= PowerThermalMitigationLevel.MinimumUserImpact)
        {
            // Note that this only kicks in at MinimumUserImpact and does not get released until NoUserImpact
            doCpuThrottle = true;
        }
    }

    if (args.ImpactedPeripherals.HasFlag(PowerThermalPeripheralFlags.PhotoVideoCamera))
    {
        SetMitigationStatus(PhotoVideoCameraStatusText, PhotoVideoRectangle, args.MitigationLevel);
    }
}

注意

参数的 ImpactedPeripherals 参数仅标识那些既受到影响又属于 PeripheralsOfInterest 的外围设备。 不会标识未包含在 PeripheralsOfInterest 中的其他受影响的外围设备。

注意

外围设备的缓解级别具有滞后性。 级别增加后,在取消之前,它不会下降。 取消是一个 args.MitigationLevel 设置为 PowerThermalMitigationLevel.NoUserImpact 的事件。

将其整合在一起(基于事件的模型)

下面是可在 Unity 中用于启用此功能的一组脚本的简单示例。 可以将 NotificationComponent 类添加到任何游戏对象,该游戏对象可以跟踪分配的外围设备的缓解级别。 NotificationManager 类通过 PowerThermalNotification 类的单个实例处理 SDK 管理订阅。

下面是 NotificationManager 类:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

using Microsoft.MixedReality.PowerThermalNotification;

public class NotificationManager
{
    private static readonly object listLock = new object();
    private static List<NotificationComponent> components = new List<NotificationComponent>();
    private static PowerThermalNotification p = PowerThermalNotification.GetForCurrentProcess();
    private static bool FirstTime = true;

    private static void NotificationHandler(object sender, PowerThermalEventArgs args)
    {
        lock (listLock)
        {
            foreach (NotificationComponent c in components)
            {
                UnityEngine.WSA.Application.InvokeOnAppThread(() =>
                {
                    c.SetMitigationLevel(args.ImpactedPeripherals, args.MitigationLevel);
                }, false);
            }
        } 
    }

    public static void ChangeSuppression(PowerThermalPeripheralFlags peripherals, bool suppress)
    {
        p.SuppressPlatformMitigation(peripherals, suppress);
    }

    public static void AddNotification(NotificationComponent component, PowerThermalPeripheralFlags peripheralsOfInterest)
    {
        if (FirstTime)
        {
            p.PowerThermalMitigationLevelChanged += NotificationHandler;
            FirstTime = false;
        }
        
        if (PowerThermalNotification.IsSupported(peripheralsOfInterest))
        {
            lock (listLock)
            {
                component.SetMitigationLevel(peripheralsOfInterest, (PowerThermalMitigationLevel)0);
                components.Add(component);
            }
            p.PeripheralsOfInterest |= peripheralsOfInterest;
        }
    }
}

下面是 NotificationComponent 类:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using Microsoft.MixedReality.PowerThermalNotification;

public class NotificationComponent : MonoBehaviour
{
    //Note that this could be multiple peripherals, just need to make sure to look at impactedPeripherals in the handler
    public PowerThermalPeripheralFlags monitoredPeripheral = (PowerThermalPeripheralFlags) 0;
    public bool isSuppressed = false;

    public void SetMitigationLevel(PowerThermalMitigationLevel level)
    {
        Color newColor = Color.white;

        if (level == PowerThermalMitigationLevel.NoUserImpact)
        {
            newColor = Color.green;
        }
        else if (level == PowerThermalMitigationLevel.MinimumUserImpact)
        {
            newColor = Color.yellow;
        }
        else if (level == PowerThermalMitigationLevel.MediumUserImpact)
        {
            newColor = new Color32(255, 127, 37, 255);//Orange
        }
        else
        {
            newColor = Color.red;
        }

        MaterialPropertyBlock props = new MaterialPropertyBlock();
        props.SetColor("_Color", newColor);
        GetComponent<Renderer>().SetPropertyBlock(props);
    }

    public void SetMitigationLevel(PowerThermalPeripheralFlags impactedPeripherals, PowerThermalMitigationLevel level)
    {
        if (impactedPeripherals.HasFlag(monitoredPeripheral))
        {
            SetMitigationLevel(level);
        }
    }

    void Start()
    {
        NotificationManager.AddNotification(this, monitoredPeripheral);
        NotificationManager.ChangeSuppression(monitoredPeripheral, isSuppressed);
    }

}

基于轮询的 API 使用情况

更新相关外围设备

与基于事件的使用情况类似,需要设置 PeripheralsOfInterest 属性才能轮询给定的外围设备。

警告

如果在没有先在 PeripheralsOfInterest 中设置该标志的情况下尝试为给定外围设备调用 GetLastPeripheralState,将会引发异常。 同样,如果尝试使用具有无效值(设置了多个标志位或不受支持的位)的 GetLastPeripheralState,也会引发异常。

调用轮询 API

PeripheralsOfInterest 设置了想要轮询的外围设备位后,就可以调用 GetLastPeripheralState

返回的 PowerThermalPeripheralState 包含给定外围设备的热评分和缓解级别的最新值。

注意

在将来的平台中,某些外围设备可能不受支持。 在这些情况下,API 将返回 100 的热评分和 NoUserImpact 的缓解级别。 应用程序可以检查结构的 IsSupportedPeripheral 字段,从而检查给定外围设备是否属于这种情况。

有关处理由 PowerThermalPeripheralState 返回的热评分缓解级别的详细信息,请参阅处理事件

下面是一个显示轮询的小代码片段:

private async void timerCallback(object state)
{
    await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        PowerThermalNotification p = PowerThermalNotification.GetForCurrentProcess();

        PowerThermalPeripheralState CpuState = p.GetLatestPeripheralState(PowerThermalPeripheralFlags.Cpu);
        PowerThermalPeripheralState PhotoVideoCameraState = p.GetLatestPeripheralState(PowerThermalPeripheralFlags.PhotoVideoCamera);
        
        CpuScoreText.Text = CpuState.ThermalScore.ToString();
        PhotoVideoScoreText.Text = PhotoVideoCameraState.ThermalScore.ToString();
    });
}

private void InitializeThermalNotifications()
{
    PowerThermalNotification p = PowerThermalNotification.GetForCurrentProcess();

    PowerThermalPeripheralFlags requestedFlags = PowerThermalPeripheralFlags.Cpu | PowerThermalPeripheralFlags.PhotoVideoCamera;
    p.SuppressedPlatformMitigationForPeripherals = requestedFlags;//Suppress any platform mitigation on CPU or PhotoVideoCamera

    if (PowerThermalNotification.IsSupported(requestedFlags))
    {
        p.PeripheralsOfInterest = requestedFlags;

        Timer timer = new Timer(timerCallback, null, 0, 3000);
    }
    else
    {
        TitleLabel.Text = "Not Supported";
    }
}

取消默认系统缓解

如果你不希望系统尝试缓解某些外设,可以取消它们。 为此,只需更新 SuppressedPlatformMitigationForPeripherals 属性,或调用 SuppressPlatformMitigation 函数。

下面是一个小代码片段:

PowerThermalNotification p = PowerThermalNotification.GetForCurrentProcess();
PowerThermalPeripheralFlags requestedFlags = PowerThermalPeripheralFlags.Cpu | PowerThermalPeripheralFlags.PhotoVideoCamera;

//You can do this to set the property explicitly
p.SuppressedPlatformMitigationForPeripherals = requestedFlags;

//Or you can do this to manipulate the property mask. 
//This specific example clears the CPU, leaving the PhotoVideoCamera suppressed
p.SuppressPlatformMitigation(PowerThermalPeripheralFlags.Cpu, false);

注意

仅当使用 PowerThermalNotification 类的进程处于前景时,取消 API 才会起作用。 后台进程仍然可以订阅事件,但无法禁用 HoloLens 2 操作。

测试

将 SDK 集成到应用程序后,对其进行测试。 对于支持 SDK 的 HoloLens 2 操作系统,设备门户中将提供一个开发人员页面。 在此页面中,你可以控制每个外围设备的缓解级别和热评分。 还可以监视哪些外围设备的缓解措施处于主动抑制状态。

你还可以利用 REST API 监视/测试其他设备的缓解级别和热评分。 可在设备门户 API 参考中找到详细信息