You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python轮询支持VRR的显示器实时刷新率?

问题:如何获取VRR显示器的实时动态刷新率?

我正在开发一款Python工具,用来轮询并显示支持VRR(G-Sync/Free-Sync)的显示器的实时VRR数值。目前的代码只能获取显示器的预设刷新率(会随系统/游戏内的刷新率设置改变,但不是动态波动的实时值),但我需要的是随场景需求动态变化的实时运行刷新率,请问该怎么实现?

当前代码:

import tkinter as tk
import win32api


class RefreshRateMonitor(tk.Tk):
    def __init__(self):
        super().__init__()
        self.label = tk.Label(self, text="Refresh rate:")
        self.label.pack()
        self.refresh_rate_timer = self.after(1000, self.update_refresh_rate)

    def update_refresh_rate(self):
        refresh_rate = get_refresh_rate(None)
        self.label.config(text=f"Refresh rate: {refresh_rate} Hz")
        self.refresh_rate_timer = self.after(1000, self.update_refresh_rate)

def get_refresh_rate(device_name):
    display_settings = win32api.EnumDisplaySettings(device_name, -1)
    refresh_rate = display_settings.DisplayFrequency
    return refresh_rate


app = RefreshRateMonitor()
app.mainloop()

解决方案

你当前用的win32api.EnumDisplaySettings获取的是显示器的标称刷新率(即系统预设的固定值),无法捕捉VRR动态调整的实时帧速率。要获取实时VRR数值,需要调用Windows的DXGI API(DirectX Graphics Infrastructure),它能直接查询显示适配器当前输出的实际帧速率。

实现步骤

  1. 用ctypes调用Windows原生的dxgi.dll接口,获取当前显示输出的实时帧率统计信息。
  2. 调整Tkinter的更新频率(从1000ms改为100ms),更贴合VRR的动态变化节奏。
  3. 通过连续两次获取帧统计数据,计算出实时刷新率。

完整代码

import tkinter as tk
import ctypes
from ctypes import wintypes

# 定义DXGI相关结构体
class DXGI_FRAME_STATISTICS_MEDIA(ctypes.Structure):
    _fields_ = [
        ("PresentCount", wintypes.UINT),
        ("PresentRefreshCount", wintypes.UINT),
        ("SyncRefreshCount", wintypes.UINT),
        ("SyncQPCTime", wintypes.LARGE_INTEGER),
        ("SyncGPUTime", wintypes.LARGE_INTEGER)
    ]

# 加载DXGI系统库
dxgi = ctypes.WinDLL("dxgi.dll")

# 定义DXGI函数的参数类型
dxgi.CreateDXGIFactory1.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)]
dxgi.IDXGIAdapter_EnumOutputs.argtypes = [ctypes.c_void_p, wintypes.UINT, ctypes.POINTER(ctypes.c_void_p)]
dxgi.IDXGIOutput_GetFrameStatisticsMedia.argtypes = [ctypes.c_void_p, ctypes.POINTER(DXGI_FRAME_STATISTICS_MEDIA)]

def get_real_time_refresh_rate(adapter_idx=0, output_idx=0):
    """获取指定显示器的实时VRR刷新率"""
    # 创建DXGI工厂实例
    factory = ctypes.c_void_p()
    if dxgi.CreateDXGIFactory1(ctypes.byref(ctypes.c_void_p(ctypes.addressof(ctypes.c_void_p()))), ctypes.byref(factory)) != 0:
        return 0.0

    # 枚举显卡适配器
    adapter = ctypes.c_void_p()
    if dxgi.IDXGIAdapter_EnumOutputs(factory, adapter_idx, ctypes.byref(adapter)) != 0:
        return 0.0

    # 枚举显示器输出
    output = ctypes.c_void_p()
    if dxgi.IDXGIAdapter_EnumOutputs(adapter, output_idx, ctypes.byref(output)) != 0:
        return 0.0

    # 连续获取两次帧统计数据用于计算刷新率
    stats_prev = DXGI_FRAME_STATISTICS_MEDIA()
    dxgi.IDXGIOutput_GetFrameStatisticsMedia(output, ctypes.byref(stats_prev))
    ctypes.windll.kernel32.Sleep(1)  # 等待极短时间确保有帧变化
    stats_curr = DXGI_FRAME_STATISTICS_MEDIA()
    dxgi.IDXGIOutput_GetFrameStatisticsMedia(output, ctypes.byref(stats_curr))

    # 计算时间差与刷新率差值
    time_diff = (stats_curr.SyncQPCTime.QuadPart - stats_prev.SyncQPCTime.QuadPart) / 10_000_000  # 转换为秒
    refresh_diff = stats_curr.PresentRefreshCount - stats_prev.PresentRefreshCount

    if time_diff > 0 and refresh_diff > 0:
        return refresh_diff / time_diff
    # 失败时 fallback到标称刷新率
    display_settings = ctypes.windll.user32.EnumDisplaySettingsW(None, -1)
    return display_settings.DisplayFrequency

class VRRMonitor(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("VRR实时刷新率监控")
        self.label = tk.Label(self, text="实时刷新率: -- Hz", font=("Arial", 20))
        self.label.pack(padx=20, pady=20)
        self.update_timer = self.after(100, self.update_refresh_rate)

    def update_refresh_rate(self):
        current_rate = get_real_time_refresh_rate()
        self.label.config(text=f"实时刷新率: {current_rate:.1f} Hz")
        self.update_timer = self.after(100, self.update_refresh_rate)

if __name__ == "__main__":
    app = VRRMonitor()
    app.mainloop()

关键说明

  • 核心原理:IDXGIOutput_GetFrameStatisticsMedia获取的PresentRefreshCount是显示器实际刷新的次数,结合时间差计算出的刷新率就是VRR动态调整的实时值。
  • 兼容性:需要Windows 10及以上版本(DXGI 1.2+支持该接口),且显示器需开启G-Sync/Free-Sync功能。
  • 多显示器适配:修改adapter_idx和output_idx参数可切换监控的显示器,默认监控主显示器。

内容的提问来源于stack exchange,提问作者Jimmy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 13:20:21