为头戴式和专用监视器生成自定义合成器应用

Windows.Devices.Display.Core API 是适用于第三方合成器和内部 Windows 组件的低级别Windows 运行时 (WinRT) API,位于所有其他公共 API 之下,用于枚举、配置和驱动 Windows 中的显示适配器和显示目标。 其思路是将显示控制器视为单独的引擎,类似于 3D 引擎和 GPU 上的媒体引擎。 此 API 负责:

  • 回答有关显示硬件 (查询,例如功能和可能的显示模式)
  • 回答有关当前配置的查询
  • 设置显示硬件 (的属性,例如显示模式)
  • 配置显示硬件 (连接的监视器的分辨率、其线路格式等)
  • 分配和扫描特殊 GPU 图面(称为主图形)
  • 允许 Direct3D 与 Windows.Devices.Display.Core API (之间的互操作,例如共享图面、围栏)

值得一提的是,Windows.Devices.Display.Core 是什么:

  • 它不是游戏或应用用于在窗口中显示内容的 API。 应用仍使用 DXGIXAML合成 APIGDI 等。
  • 它不是游戏或应用用于全屏显示内容的 API。 应用仍使用 DXGI,Win32 应用仍使用 HWND,UWP 应用始终在 CoreWindow 中显示内容。

此 API 仅适用于驱动专用硬件的合成器应用。

显示专用显示器的 API 体系结构层的关系图。

生成自定义合成器的方案

Windows.Devices.Display.Core API 适合在以下方案中使用:

  • 虚拟和增强现实显示器,需要专有合成器直接驱动显示控制器,并接收与 Windows 桌面分开的计时和模式配置的精细控制。
  • 在商业设置中需要对显示器进行专用控制的专用显示硬件方案。 例如,在 Windows 桌面由于硬件扭曲、灰度显示器等原因而无法在此类显示器上正确呈现的情况下。
  • 专用的“设备”方案:监视器可能完全专用于应用,而长时间 (Windows 桌面体验 (例如专用视频监视器) 。

API 通过以下方式实现此目的:

  • 提供对完整显示模式信息(包括线路格式、HDR 等)的精细控制。
  • 使用围栏同步演示文稿,合成器可以跨进程或子组件链接演示文稿,几乎不会造成性能开销。
  • 提高查询和配置基础视频演示网络的能力 (VidPN) ,以允许系统组件和低级别组合组件以更易出错且更具可扩展性的方式执行更复杂的操作。

请注意,此 API 仅适用于具有专用硬件的一 组非常具体的 第三方用例。 它的使用高度仅限于声明自己需要此 API 功能的硬件。 因此,开发人员需要对硬件概念有一定程度的熟悉,合作伙伴应直接与 Microsoft 联系以解决问题。

硬件和软件要求

第三方自定义合成器只能获取已预先指定为头戴式显示器的显示器, (HMD) 或“专用”显示器。 必须通过以下两种方式之一提供此指定:

  • EDID 扩展 - 设计为永久用作 HMD、X 射线监视器、视频墙或其他专用方案的自定义显示设备应 实现头戴式和专用显示器的 Microsoft EDID 扩展
  • 用户替代 - 对于使用现成监视器的自定义硬件安装,Windows 提供了用于将监视器指定为“专用”的 UI 切换。

显示器 不能 通过覆盖软件中的 EDID 来指定为 HMD 或专用显示器。

注意

专用显示器仅在 Windows 10 版本 2004 开始可用,需要Windows 10 企业版、Windows 10 专业工作站版或Windows 10 IoT 企业版。

实现自定义合成器路线图

实现自定义合成器可以分为几个阶段:

  • 枚举和发现关联的 HMD 或专用显示器
  • 获取所选显示器的所有权
  • 为所有选定显示器配置模式
  • 创建用于向显示器呈现帧的资源
  • 呈现内容和计划帧呈现
API 目的和目标受众
DisplayInformation 用于检索 CoreWindow 的呈现和布局属性。
HdmiDisplayInformation 仅限 Xbox 的 API,用于枚举和设置一组受约束的模式。 高度专用于 Xbox 媒体应用方案。
DisplayMonitor 用于查询物理监视器设备的属性。 不公开有关 OS 如何配置或当前使用监视器的任何运行时信息。
EnumDisplayDevicesEnumDisplayMonitorsEnumDisplaySettingsEx 用于查询 HMONITOR、GDI 设备和物理监视器映射的旧版 Win32 API。 此处返回的信息经过高度虚拟化和维护,以确保应用程序兼容性。
Direct3D 用于将像素内容呈现到 GPU 图面并在 GPU 上执行计算。
DXGI 交换链 用于开窗和无边框的全屏演示文稿。 应用交换链内容流经系统合成器 DWM。
DXGI 输出枚举 在 HMONITOR 周围提供 DXGI 包装器。
QueryDisplayConfigSetDisplayConfigDisplayConfigGetDeviceInfoDisplayConfigSetDeviceInfo 用于配置显示拓扑的 Win32 API。 不提供枚举多个模式的机制,但具有有关当前配置和设置的丰富信息集。 但是,并非所有模式的较新属性都由这些 API 公开。
Windows.Devices.Display.Core (本文档) 用于枚举目标、枚举模式、配置模式、分配用于演示的 GPU 图面以及向显示器呈现内容。

显示配置概述

物理硬件枚举

Windows.Devices.Display.Core API 具有各种对象,用于表示物理硬件对象。 DisplayAdapter 通常 (但并不总是) 物理硬件设备,例如 PCI Express 连接的 GPU 或 CPU 上的集成 GPU。 DisplayTarget 对象表示可从 GPU 连接到的物理连接器 (,例如 HDMI、VGA、DisplayPort 等 ) 。 这可能包括具有内部监视器 (笔记本电脑、平板电脑等 ) 设备的内部非用户可见连接。 软件中表示的 DisplayTarget 对象可能多于用户一次可以进行物理连接。 例如,由于 DisplayPort 连接标准允许菊花链,因此 GPU 驱动程序通常会枚举每个物理端口的多个 DisplayPort 目标,以便考虑链接的监视器。

显示适配器和显示目标的硬件拓扑图示。

用于设置模式的对象

为了枚举 DisplayTarget 对象,设置和查询模式等,使用 DisplayPath 对象表示与 DisplayTarget 对象的连接。 显示相同内容 (克隆组) 的路径组由 DisplayView 表示,这些路径将聚合为 DisplayState。 因此,一个 DisplayState 对象可以表示一组完整的模式状态,这些状态可以发送到多个监视器的驱动程序。

包含显示路径、显示视图和显示状态对象的模式拓扑图示。

模式配置和枚举的原子状态

Windows.Devices.Display.Core API 旨在确保合成器可以通过原子方式获取对各种系统显示状态的访问权限,并且具有明确定义的过时行为。 这一点非常重要,因为 GPU 是共享资源,带宽和电源约束非常严格。 在现代系统中,设备可能随时到达/离开,其他事项可能会影响可用显示模式的列表 (例如停靠/取消停靠、睡眠状态、另一个组件在另一条路径上更改模式) 。 因此,使用 Windows.Devices.Display.Core API 并遵循配置状态的建议模式,组合器必须能够复原系统配置更改。

因此,Windows.Devices.Display.Core API 提供简单的事务性读取-修改-提交模型,类似于数据库。 客户端可以原子方式读取系统中显示设备的 DisplayState 对象。 所有对象都是不可变的,或者提供定义完善的 API,以更新/提交回系统的状态。 在调用 DisplayState.TryApply (“提交”对系统的更改)之前,不会进行更改。 向 DisplayState 提交/应用更改失败且无影响,或成功应用完整更改。

若要利用 API 的原子性功能,请执行以下操作:

  • 重试循环中写入任何模式配置逻辑。
  • 请在 模式配置开始时在每个循环内创建新的 DisplayState。
  • 调用DisplayState.TryApply 时,请使用 FailIfStateChanged 标志来检测系统状态不再与创建 DisplayState 时的状态相同。 这样,你就有机会重试该操作。 如果操作在 SystemStateChanged 中失败,请重试整个循环。
  • 请勿 将读取或更改状态的其他 API (DXGI、GDI 等 ) 混合使用 Windows.Devices.Display.Core API,因为它们可能不具有相同的原子性保证。
#include <winrt\Windows.Devices.Display.Core.h>
using namespace winrt::Windows::Devices::Display::Core;
...

// Create a DisplayManager
DisplayManager manager = DisplayManager::Create(DisplayManagerOptions::EnforceSourceOwnership);

// Loop around trying to acquire a target and set a mode
bool shouldRetry;
do
{
    shouldRetry = false;

    // ... Find the target that you want to use
    auto targets = manager.GetCurrentTargets();
    DisplayTarget selectedTarget = ...;

    auto stateCreationResult = manager.TryAcquireTargetsAndCreateEmptyState(
        winrt::single_threaded_vector<DisplayTarget>({ selectedTarget }));

    if (stateCreationResult.ErrorCode() != DisplayManagerResult::Success)
    {
        winrt::check_hresult(stateCreationResult.ExtendedErrorCode());
    }

    auto state = stateCreationResult.State();
    DisplayPath newPath = state.ConnectTarget(selectedTarget);

    // ... Configure the path

    auto applyResult = state.TryApply(DisplayStateApplyOptions::FailIfStateChanged);

    if (applyResult.Status() == DisplayStateOperationStatus::SystemStateChanged)
    {
        shouldRetry = true;
    }
    else if (applyResult.Status() != DisplayStateOperationStatus::Success)
    {
        winrt::check_hresult(applyResult.ExtendedErrorCode());
    }

} while (shouldRetry);

以下 API 以原子方式从系统读取状态:

以下 API 将状态提交回系统:

  • DisplayManager
  • DisplayState
    • TryApply 通过显示驱动程序在系统中所有拥有的目标上设置或清除模式,汇报当前系统显示状态。

已知的限制

自 Windows 10 版本 2004) 起,Windows.Devices.Display.Core API 有几个已知的限制 (:

  • 间接显示驱动程序 (例如 Miracast、USB 显示适配器、软件驱动程序) 目前无法寻址。 传递间接显示适配器时,DisplayManager.CreateDisplayDevice 将失败。

代码示例

有关示例应用程序,请参阅 Windows.Devices.Display.Core 自定义合成器示例