本文介绍从 Windows 11 开始支持的硬件翻转队列功能(WDDM 3.0)。 硬件翻转队列允许将多个将来的帧提交到显示控制器队列。 当显示控制器正在处理多个排队帧时,GPU 的 CPU 和部分可以转换为较低电源状态,从而提高支持硬件上的视频播放方案的电源效率。
WDDM 3.0 之前的翻转队列模型
许多新式显示控制器支持对序列中显示的多个帧进行排队的功能。 从 WDDM 2.1 开始,操作系统支持多个待处理的翻转覆盖请求,这些请求将在下一个 VSync 上呈现。 显示微型端口驱动程序 (KMD) 通过 DXGK_DRIVERCAPS 中的 MaxQueuedMultiPlaneOverlayFlipVSync 值指示此支持。 此功能可用于降低高帧速率游戏方案中的延迟,其中多个帧按间隔 0 按顺序呈现,目的是仅显示最近的帧。
在视频播放场景中,多个要按顺序显示的未来帧的内容是提前已知的,并可以提交到 GPU。 这种提前排队允许 CPU 在处理排队帧时进入低功率状态,从而节省大量电源。 然而,在 WDDM 3.0 之前,操作系统没有机制可以提交多个帧,这些帧需要至少一个 VSync 间隔保持在屏幕上,而无需进一步的 CPU 干预。 基本硬件翻转队列部分引入了一种解决方案,使 CPU 能够进入低功率状态,并将排队帧处理卸载到 GPU。
在 WDDM 3.0 之前的游戏场景中,GPU 完成将场景呈现到交换链后缓冲区后,将请求提交给 CPU 处理,以将帧内容显示在屏幕上。 对于接近 VSync 的繁重 GPU 工作负荷,此往返可能会导致帧延迟并错过预期目标时间,从而导致可观察的帧故障。 “高级硬件翻转队列”部分引入了一种机制,用于避免 CPU 的来回操作,并以低延迟将已完成的帧呈现给屏幕。 高级硬件翻转队列同时需要存在基本硬件翻转队列和 GPU 硬件调度阶段 2 功能。
基本硬件翻转队列
下图演示了三个帧的呈现情况,其中每个帧都在屏幕上停留一个 VSync 间隔。
图表中的填充模式显示 Dxgkrnl 软件翻转队列处理过程和应用线程必须唤醒并执行 CPU 工作的时间。 在每个 VSync 上,显示控制器必须向 OS 发出 CPU 通知才能完成翻转,并且 OS 必须提交下一个翻转请求。 应用程序还必须在每个 VSync 上唤醒并查询当前帧呈现的统计数据,以便最终知道三帧中的最后一帧何时显示。
从 WDDM 3.0 开始,可以向显示控制器队列提交多个未来帧的硬件翻转队列 DDI 可用。 如前所述,此机制允许 GPU 的 CPU 和部分在显示控制器处理多个排队帧时转换到较低电源状态。 此转换可提高支持硬件的视频播放方案的电源效率。
下图说明了建议的体系结构。
借助硬件翻转队列方法,应用程序和 Dxgkrnl CPU 组件在 v2 和 v4 之间的两个 VSync 间隔处于完全空闲状态,使 CPU 能够进入低功率状态。 只有当应用程序请求的帧N+2完成等待时,CPU 才会收到通知。
高级硬件翻转队列
在 WDDM 3.0 之前的游戏场景中,在 GPU 完成将场景渲染到交换链后缓冲区后,需要通过 CPU 进行一次往返,以便将请求提交,以将帧内容呈现到屏幕上。 下图显示了此方案。
如果渲染完成时离 VSync 太近,此往返成本可能会导致帧错过其目标,如下图所示。
某些显示控制器原生支持等待条件机制,允许显示在 GPU 完成帧渲染后提交翻转请求,而不需 CPU 的往返操作。 由于硬件翻转队列可以在无需 CPU 往返的情况下将已完成的帧 N 提交到显示上,因此它可能会避免丢帧,如下图所示。
本文的其余部分讨论基本硬件翻转队列功能。
DDI 支持
添加了以下 DDI 以支持硬件翻转队列功能。
检查功能可用性
硬件队列翻转需要与操作系统协商启用或禁用。 支持硬件翻转队列的 KMD 必须在设备启动期间,首次调用 DXGKCB_QUERYFEATURESUPPORT,并使用 FeatureId 为 DXGK_FEATURE_HWFLIPQUEUE,以确定操作系统是否允许启用硬件翻转队列。
仅当回调成功且 “启用 ”设置为 TRUE 时,才能使用硬件翻转队列。
KMD 可以在硬件翻转队列初始化和实验阶段使用以下示例代码。
DXGKARGCB_QUERYFEATURESUPPORT HwFlipQueueEnabledArgs = {};
HwFlipQueueEnabledArgs.DeviceHandle = DeviceHandle;
HwFlipQueueEnabledArgs.FeatureId = DXGK_FEATURE_HWFLIPQUEUE;
HwFlipQueueEnabledArgs.DriverSupportState = DXGK_FEATURE_SUPPORT_EXPERIMENTAL;
if (!NT_SUCCESS(pDxgkInterface->DxgkCbQueryFeatureSupport(&HwFlipQueueEnabledArgs)) ||
!HwFlipQueueEnabledArgs.Enabled)
{
// Disable hardware flip queue because the OS didn't allow it.
}
else
{
// Enable hardware flip queue because the OS allowed it.
}
在驱动程序准备过程中,尽管可以在不启用 GPU 硬件调度的情况下启用硬件翻转队列,但这组组合没有得到官方支持。 Windows 当前要求启用 GPU 硬件调度,以便在正式发布的驱动程序上启用基础硬件翻转队列。
指示硬件队列功能
MaxHwQueuedFlips 已添加到 DXGK_DRIVERCAPS ,以指示硬件翻转队列支持。 如果 OS 根据先前描述允许硬件翻转队列支持,则支持硬件翻转队列的 KMD 应将 MaxHwQueuedFlips 设置为大于 1 的值。 当 MaxHwQueuedFlips 大于 1 时,KMD 指示显示硬件最多支持 MaxHwQueuedFlips 未来的帧,这些帧可以排队等待 GPU 上的给定 VidPnSource 显示。 OS 遵循驱动程序提供的关于翻转类型的限制,允许提前排队。
HwQueuedFlipCaps 也添加到 DXGK_DRIVERCAPS。 此成员当前保留供系统使用;驱动程序开发人员不应使用此成员。
翻转目标时间和目标时间戳格式
当 OS 向硬件翻转队列提交翻转请求时,它还会发送目标翻转时间。 在达到目标翻转时间后,翻转会对用户可见。
OS 使用 从 KeQueryPerformanceCounter 获取的 CPU 时钟计数器单元传递目标帧时间并解释实际帧时间。
提交已排队的任务
DXGKARG_SETVIDPNSOURCEADDRESSWITHMULTIPLANEOVERLAY3 结构被传递给 KMD 的 DxgkDdiSetVidPnSourceAddressWithMultiPlaneOverlay3 回调函数,并进行了如下修改,以便启用排队翻转的提交:
以下三个成员已添加到 OutputFlags 的 DXGK_SETVIDPNSOURCEADDRESS_OUTPUT_FLAGS 结构中。 有关这些成员的详细信息,请参阅 DxgkDdiSetVidPnSourceAddressWithMultiPlaneOverlay3 重试和失败案例 。
- HwFlipQueueDrainNeeded
- HwFlipQueueDrainAllPlanes
- HwFlipQueueDrainAllSources
添加了 TargetFlipTime 成员。 TargetFlipTime 描述 QPC 单元中的目标翻转时间。 当时钟达到此值时,帧可以发送到显示器,同时遵循 VSync 和撕裂标志。 如果存在先前排队的挂起翻转,OS 保证对于翻转请求引用的每个 MPO 平面,TargetFlipTime 大于或等于任何该平面的挂起翻转的目标时间。 换句话说,可以有一系列具有相同或递增时间戳的切换,但不能有一个时间倒退的序列。
DxgkDdiSetVidPnSourceAddressWithMultiPlaneOverlay3 重试和失败案例
由于待处理的翻转,无法将请求排队到硬件
有几个特殊情况可能会阻止 KMD 在其他翻转请求挂起期间对翻转请求进行排队。 在这种情况下,KMD 应从 DxgkDdiSetVidPnSourceAddressWithMultiPlaneOverlay3 返回STATUS_RETRY,并将 HwFlipQueueDrainNeeded 设置为 1。 OS 将尝试在完成受翻转影响的平面上所有挂起的翻转后再次提交翻转请求,并在达到目标时间后提交翻转请求。
在某些情况下,显示硬件可能需要在所有平面上完成待处理的翻转,而不仅仅是完成传入翻转请求所引用的翻转。 在这种情况下, HwFlipQueueDrainNeeded 和 HwFlipQueueDrainAllPlanes 标志应设置为 1,KMD 应返回STATUS_RETRY。
同样,显示硬件可能需要完成所有 VidPn 源中挂起的翻转操作,以便重新分配内部资源。在这种情况下,必须设置 HwFlipQueueDrainAllSources 和 HwFlipQueueDrainNeeded 标志,并且 KMD 应该返回 STATUS_RETRY。
此外,KMD 可以向 OS 指示是否应在设备 IRQL(PrePresentNeeded 设置为 0)上完成重新提交,或者 OS 是否应在PASSIVE_LEVEL(PrePresentNeeded 设置为 1)执行此调用。 如果 KMD 仍然返回 STATUS_RETRY,尽管该 VidPnSourceId 上没有更多挂起的翻转,此条件被视为 无效的参数失败。
重要的是 MaxHwQueuedFlips 的值仍反映可排队到 MPO 平面的简单仅地址更改翻转的最大数量。 STATUS_RETRY机制应该用于那些无法进行深度排队的较复杂的翻转请求,例如平面配置更改。
无效参数错误
在硬件翻转队列模型中,OS 对失败的翻转请求的处理已重新工作,以提供更好的可调试性。 当 KMD 无法处理翻转请求时,它应从 DxgkDdiSetVidPnSourceAddressWithMultiPlaneOverlay3 返回STATUS_INVALID_PARAMETER。 根据 OS 设置,OS 将执行以下作之一:
- 内核调试器中断和故障检查:在开发/预发布版本中,常常启用这种行为,以便在故障发生时更好地提供调试功能。
- 实时内核转储后紧接着发生 TDR:零售终端用户行为。
指定 VSync 中断行为
为了在队列翻转的应用场景中实现节省电力,操作系统通常会暂停常规的VSync中断,保持CPU处于低功耗状态。 但是,某些翻转被标记为需要引发中断,以便应用程序观察已完成的演示批次并排队进一步工作。 在某些情况下,应用程序请求在每个 VSync 中断时唤醒,而不考虑是否有挂起的翻转请求。 相反,在完全空闲的系统上,VSync 中断将暂停,直到出现新的演示活动或 VSync 侦听器。
为了处理所有这些情况,引入了以下驱动程序回调和回调结构:
KMD 在 DRIVER_INITIALIZATION_DATA 中提供指向其 DxgkDdiSetInterruptTargetPresentId 函数的指针
OS 调用 DxgkDdiSetInterruptTargetPresentId 来指定目标 PresentId,该目标 PresentId 应在相应翻转完成后引发 VSync 中断。 此函数在设备中断级别(DIRQL)调用,以便与 DxgkDdiSetVidPnSourceAddress 和 VSync 中断同步。
与 DxgkDdiControlInterrupt 交互
当通过 DxgkDdiControlInterrupt/DxgkDdiControlInterrupt2/DxgkDdiControlInterrupt3 完全禁用 VSync 中断时,无论中断目标 PresentId 值如何,它们都会保持禁用状态。 KMD 需要存储最新的中断目标存在 ID,以便在再次启用 VSync 后可以遵循该 ID。
通过 DxgkDdiControlInterruptXxx 启用 VSync 中断时,中断目标呈现 ID(pSetInterruptTargetPresentId)提供精细的控制,如下所示:
当目标呈现 ID 设置为 UINT64_MAX 时,不需要任何 VSync 中断,直到目标呈现 ID 再次更改为止。 VSync 中断已禁用,但需要 KMD 来实现 DXGK_VSYNC_DISABLE_KEEP_PHASE 行为才能重新启用中断。
当目标存在 ID 设置为 0 时,每个 VSync 都需要中断。
对于任何其他当前 ID 值,如果当前扫描的 PresentId >= InterruptTargetPresentId,则会引发中断。
当多个 MPO 平面可用时,如果任何平面需要进行 VSync 中断,则应引发 VSync 中断。
使用 DxgkDdiSetInterruptTargetPresentId 禁用双阶段 VSync
如果 OS 调用 DxgkDdiSetInterruptTargetPresentId 在一个平面上设置 InterruptTargetPresentId,从而导致在这个 VidPnSource 上完全禁用 VSync(也就是说,这个平面是保持 VSync 启用的最后一个平面,现在它也禁用 VSync),KMD 应该禁用 VSync 中断,但在硬件中保持 VSync 阶段启用(DXGK_VSYNC_DISABLE_KEEP_PHASE)。 在一定时间段(通常相当于两个 VSync 期间)之后,OS 将跟进调用 DxgkDdiControlInterruptXxx 并附带 DXGK_VSYNC_DISABLE_NO_PHASE。 此调用可确保 KMD 有机会禁用 VSync 阶段和 VSync 时钟,以节省最大功率并维护与非硬件翻转队列系统的性能奇偶校验。
队列翻转取消
在全屏状态转换或应用程序退出等情况下,可能需要取消已排队的未来任务切换。 为了处理这些情况,引入了以下驱动程序回调和相关结构:
KMD 在 DRIVER_INITIALIZATION_DATA 中提供指向其 DxgkDdiCancelFlips 函数的指针。
OS 指定在调用 DxgkDdiCancelFlips 时要取消的排队翻转的范围,而 KMD 则向 OS 报告其能够同步取消的具体翻转范围。
下面的示例演示了单平面上翻转抵消的原理和同步情况。 (操作系统不支持在 Windows 11 版本 22H2 中异步取消。)请想象以下翻转正在排队到硬件翻转队列:
- PresentId N
- time t0 PresentId N+1
- time t1 PresentId N+2
- time t2 PresentId N+3
- time t3 PresentId N+4
- 时间 t4
然后,OS 决定取消翻转 N+2、N+3 和 N+4,因此它调用将 PresentIdCancelRequested 设置为 N+2 的 DxgkDdiCancelFlips。
当 KMD 检查硬件翻转队列的状态时,会得出以下结论:
- 翻转 N+2 已发送到显示硬件,在调用时无法取消。
- 可以同步从硬件翻转队列中删除 N+3 和 N+4 ,而不会产生副作用。
因此,KMD 将 PresentIdCancelled 设置为 N+3 ,并照常完成 N+2 。
OS 将 N+3 和 N+4 标记为已取消,它将 N、 N+1、 N+2 视为正在飞行。 引发下一个 VSync 中断时,翻转队列日志将像往常一样指示 N、 N+1、 N+2 的时间戳。
同步取消的翻转的范围必须是连续的,如果不是零,则假定包含提交到 KMD 的最后一个当前 ID。 换句话说,在同步取消的两个翻转范围内不能有间隙。
取消多平面上的互锁翻转
通过调用 DxgkDdiSetVidPnSourceAddress 并使用多个图层和 PresentIds 提交交锁翻转。 OS 和 KMD 之间的协定如下:
- 必须在同一 VSync 上使平面集合可见。
- 不允许显示设备在一个 VSync 上仅显示这些平面中的子集,在下一个 VSync 上显示其余部分。
在硬件翻转队列模型中,通过在调用 DxgkDdiCancelFlips 时传递多个平面和多个 PresentIds 来取消此类互锁翻转。 在这种情况下传递的平面集必须与挂起的联锁翻转请求相对应,并且 KMD 关于所有联锁 PresentId 的决定必须相同:
- 请勿取消,否则会有后果。
- 同步取消
在设备中断级别(DIRQL)调用 DxgkDdiCancelFlips,以实现与 DxgkDdiSetVidPnSourceAddress 及 VSync 中断的同步。
获取排队翻转的当前统计信息
由于硬件翻转队列方法是避免在每个 VSync 上唤醒 CPU,因此需要有一种机制来保留最后几个排队翻转的帧显示时间。
支持硬件翻转队列的图形驱动程序必须针对每个活动的 VidPnSource 的指定 MPO 平面,将每个已完成或取消的翻转的信息写入操作系统提供的翻转队列日志缓冲区。
OS 保证在首次调用 DxgkDdiSetVidPnSourceAddress 之前,通过 DxgkDdiSetFlipQueueLogBuffer 提供翻转队列日志指针,针对每个活动的 VidPnSource 的指定 MPO 平面。 当翻转队列没有任何未完成的请求时,允许 OS 销毁翻转队列日志缓冲区。 在这种情况下,它将在下一个 DxgkDdiSetVidPnSourceAddress 调用之前提供新的日志指针。 翻转队列日志具有循环结构。 写入 [NumberOfEntries-1] 条目后,下一个日志条目为 [0]。
完成一批排队翻转后,KMD 必须保证在上述两个时间点中最早更新已完成翻转的翻转队列日志:
- 需要进行中断以完成翻转的 VSync 中断处理程序。
- 响应来自 OS 的显式 DxgkDdiUpdateFlipQueueLog 请求。
翻转队列日志 DDIs
添加了以下与翻转队列日志相关的回调及其相关结构:
KMD 在DRIVER_INITIALIZATION_DATA中提供指向其函数的指针。
VSync 中断结构更新
对 DXGKARGCB_NOTIFY_INTERRUPT_DATA 结构进行了以下更改,以实现硬件翻转队列模型的 VSync 中断:
- DXGK_INTERRUPT_CRTC_VSYNC_WITH_MULTIPLANE_OVERLAY3枚举值已添加为 InterruptType。
- CrtcVSyncWithMultiPlaneOverlay3 结构已添加到联合中。 CrtcVSyncWithMultiPlaneOverlay3 的语义类似于现有的 CrtcVSyncWithMultiPlaneOverlay2 结构,不同之处在于,CrtcVSyncWithMultiPlaneOverlay3.pMultiPlaneOverlayVSyncInfo 指向翻转队列日志中多个之前未报告的 PresentId 的范围。
- 为 CrtcVSyncWithMultiPlaneOverlay3 的 pMultiPlaneOverlayVSyncInfo 成员添加了DXGK_MULTIPLANE_OVERLAY_VSYNC_INFO3结构。
再次使用 基本硬件翻转队列 示例图:
假设 FirstFreeFlipQueueLogEntryIndex 在提交翻转 N 时设置为 40,然后已完成 N、 N+1、 N+2 演示。
在单个平面配置分别在时间点 v2、v3、v4 完成三个 PresentIds N、N+1 和 N+2 后,KMD 在其翻转队列日志缓冲区中写入了索引为 40、41 和 42 的三个新条目。 KMD 报告 CrtcVSyncWithMultiPlaneOverlay3 结构中的 FirstFreeFlipQueueLogEntryIndex 值为 43。 OS 观察到 FirstFreeFlipQueueLogEntryIndex 从 40 更改为 43,从日志条目 40、41 和 42 读取。 KMD 需要设置以下翻转队列日志缓冲区值,如下所示:
VidPnTargetId:与 CrtcVSyncWithMultiPlaneOverlay2 中的含义相同
PhysicalAdapterMask:与 CrtcVSyncWithMultiPlaneOverlay2 中的含义相同
MultiPlaneOverlayVSyncInfoCount = 1
pMultiPlaneOverlayVSyncInfo[0]。LayerIndex = 0
pMultiPlaneOverlayVSyncInfo[0]。FirstFreeFlipQueueLogEntryIndex = 43
LogBufferAddressForPlane0[40]。PresentId = N
LogBufferAddressForPlane0[40]。PresentTimestamp = v2
LogBufferAddressForPlane0[41]。PresentId = N+1
LogBufferAddressForPlane0[41]。PresentTimestamp = v3
LogBufferAddressForPlane0[42]。PresentId = N+2
LogBufferAddressForPlane0[42]。PresentTimestamp = v4
显式翻转队列日志更新请求
在某些情况下,OS 需要获取有关上次完成的翻转批的信息,而无需等待 VSync 中断。 在这种情况下,操作系统显式调用 DxgkDdiUpdateFlipQueueLog 来请求 KMD 从其专有显示硬件数据结构中读取,并将先前的翻转信息写入翻转队列日志。 日志的语义与前面所述的语义相同;唯一的更改是在 VSync 中断之外返回 FirstFreeFlipQueueLogEntryIndex 给 OS。
DxgkDdiUpdateFlipQueueLog 在设备中断级别(DIRQL)调用,它与 DxgkDdiSetVidPnSourceAddressWithMultiPlaneOverlay3 DDI 位于同一同步类中。
在硬件翻转队列中存在排队翻转时显示模式更改和电源转换
Dxgkrnl 确保在进行模式更改或关闭显示器之前,硬件翻转队列中已排队的翻转已被完成或取消。
将 Present 请求映射到硬件翻转队列时间戳
在特定适配器上启用硬件翻转队列时,所有翻转调用都会附带一个时间戳。 换句话说,KMD 不需要处理旧 和新的 DxgkDdiSetVidPnSourceAddress 语义的组合。
OS 会自动将基于间隔的现有 Present API 请求转换为对 KMD 的基于时间戳的翻转调用。 以下各节讨论了各种情况,以及如何映射到 KMD 接收的标志、 持续时间和时间戳的组合。
撕裂和非撕裂翻转语义
启用硬件翻转队列时,撕裂翻转的语义在概念上是相同的。 达到 TargetFlipTime 后,KMD 应提交翻转以进行显示,同时仍然需要遵循以下标志:FlipImmediate、FlipImmediateNoTearing 和 FlipOnNextVSync。 换句话说,KMD 的行为应当如同 OS 在精确地 TargetFlipTime 时提交翻转,并带有相同的翻转标志和参数。
例如,如果将 FlipOnNextVSync 设置为 1,并且 TargetFlipTime 位于帧中间,则翻转应仅显示在下一个 VSync 上。
FlipOverwrite 支持和硬件翻转队列
硬件翻转队列是由 DXGK_DRIVERCAPS 中 MaxQueuedMultiPlaneOverlayFlipVSync 值控制的翻转覆盖功能的严格超集。
因此,如果驱动程序通过将 MaxHwQueuedFlips 设置为大于 1 的值选择进入硬件翻转队列,OS 将忽略 MaxQueuedMultiPlaneOverlayFlipVSync 的值。
包含已过期 TargetFlipTime 的多个翻转
当给定 MPO 平面有多个已过期 的 TargetFlipTime 的排队翻转时,硬件显示队列必须选取最近排队的过期翻转并将其提交以显示。 过期翻转的其余部分应被视为已取消,其对应的翻转队列日志条目应包含 DXGK_HWFLIPQUEUE_TIMESTAMP_CANCELLED 作为 PresentTimestamp 值。
Duration 和 TargetFlipTime 之间的交互
在屏幕上显示此结构中指定的翻转时,DXGKARG_SETVIDPNSOURCEADDRESSWITHMULTIPLANEOVERLAY3结构的Duration参数应生效。 它指定了由 VidPnSourceId 在所有平面上规定的输出的新显示刷新率行为。 在 WDDM 3.1 和 Windows Server 2022 版本中,为了简化不支持排队自定义 持续时间 更改的硬件的驱动程序实现,OS 仅在完成以前的翻转请求后提交具有新的 Duration 参数的翻转请求。
将当前间隔映射到 TargetFlipTime
固定刷新速率时的映射间隔
若要保留现有的当前间隔语义,OS 必须使用当前间隔和刷新速率计算目标翻转时间。 但是,将目标翻转时间精确设置为应与屏幕 VSync 时间一致,这会导致频繁出现故障。 这些问题是由于实际 VSync 计时稍微偏移时错过了 VSync。 为了防止故障,OS 会从计算的目标翻转时间中减去 VSync 间隔的一半。
下面是一个简化的公式,用于将当前间隔映射到目标翻转时间:
TargetFlipTime = PreviousFlipStartVSyncTime + (PreviousFlipPresentInterval * FixedRefreshRate) - (FixedRefreshRate / 2)
虚拟刷新速率 WDDM 2.9 功能出现时的映射间隔
虚拟刷新速率功能可能会暂时将显示刷新率提升到当前刷新速率的整数倍数(即 24 Hz 可以提升到 144 Hz 或 192 Hz)。 对于能够支持此提升的设备,将修改上一部分中的公式,以使用当前刷新率中最快的倍数:
TargetFlipTime = PreviousFlipStartVSyncTime + (PreviousFlipPresentInterval * FixedRefreshRate) - (FastestRefreshRate / 2)
将刷新率更改为非倍数时的映射间隔
当刷新率更改为当前刷新率的非倍数时(例如,从 24 Hz 更改为 60 Hz),操作系统需要检查队列翻转是否其计算的目标时间对新的刷新率仍然有效。 如果需要更改目标翻转时间,OS 将取消排队翻转,并使用新计算的目标翻转时间重新排队。