4

我正在将我的代码从 D3D11 移植到 D3D12,并且我正在尝试在 D3D12 上获取显示器的刷新率。我使用刷新率来进行精确的动画计时(这是一个硬性要求)。此代码适用于 D3D11:

HRESULT GetRefreshRate(IUnknown* device, IDXGISwapChain* swapChain, double* outRefreshRate)
{
    Microsoft::WRL::ComPtr<IDXGIOutput> dxgiOutput;
    HRESULT hr = swapChain->GetContainingOutput(&dxgiOutput);
    if (FAILED(hr))
        return hr;

    Microsoft::WRL::ComPtr<IDXGIOutput1> dxgiOutput1;
    hr = dxgiOutput.As(&dxgiOutput1);
    if (FAILED(hr))
        return hr;

    DXGI_MODE_DESC1 emptyMode = {};
    DXGI_MODE_DESC1 modeDescription;
    hr = dxgiOutput1->FindClosestMatchingMode1(&emptyMode, &modeDescription, device);

    if (SUCCEEDED(hr))
        *outRefreshRate = (double)modeDescription.RefreshRate.Numerator / (double)modeDescription.RefreshRate.Denominator;

    return hr;
}

不幸的是,ID3D12Device 没有实现 IDXGIDevice 接口,因此 FindClosestMatchingMode1 失败并出现以下错误:

DXGI ERROR: IDXGIOutput::FindClosestMatchingMode: pConcernedDevice doesn't support the IDXGIDevice interface [ MISCELLANEOUS ERROR #69: ]

使用D3D12时有没有办法获取IDXGIDevice?或者,如何确定 D3D12 上的显示刷新率?

但是我知道EnumDisplaySettings它返回一个整数,因此缺乏精度,导致动画漂移。但是,我还发现DwmGetCompositionTimingInfo,它似乎只支持获取主监视器的信息。

我还需要一个适用于传统 Win32 和 UWP 应用程序的解决方案。如果需要,我愿意为不同的应用程序模型使用两个代码路径。

4

1 回答 1

6

我们可以使用 CCD api 获取刷新率,下面是代码供您参考:

HRESULT GetRefreshRate(IDXGISwapChain* swapChain, double* outRefreshRate)
{
       ComPtr<IDXGIOutput> dxgiOutput;
       HRESULT hr = swapChain->GetContainingOutput(&dxgiOutput);
       // if swap chain get failed to get DXGIoutput then follow the below link get the details from remarks section
       //https://docs.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgiswapchain-getcontainingoutput
       if (SUCCEEDED(hr))
       {

          ComPtr<IDXGIOutput1> dxgiOutput1;
          hr = dxgiOutput.As(&dxgiOutput1);
          if (SUCCEEDED(hr))
          {
                 // get the descriptor for current output
                 // from which associated mornitor will be fetched
                 DXGI_OUTPUT_DESC outputDes{};
                 hr = dxgiOutput->GetDesc(&outputDes);
                 if (SUCCEEDED(hr))
                 {

                        MONITORINFOEXW info;
                        info.cbSize = sizeof(info);
                        // get the associated monitor info
                        if (GetMonitorInfoW(outputDes.Monitor, &info) != 0)
                        {
                               // using the CCD get the associated path and display configuration
                               UINT32 requiredPaths, requiredModes;
                               if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &requiredPaths, &requiredModes) == ERROR_SUCCESS)
                               {
                                      std::vector<DISPLAYCONFIG_PATH_INFO> paths(requiredPaths);
                                      std::vector<DISPLAYCONFIG_MODE_INFO> modes2(requiredModes);
                                      if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &requiredPaths, paths.data(), &requiredModes, modes2.data(), nullptr) == ERROR_SUCCESS)
                                      {
                                             // iterate through all the paths until find the exact source to match
                                             for (auto& p : paths) {
                                                    DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName;
                                                    sourceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
                                                    sourceName.header.size = sizeof(sourceName);
                                                    sourceName.header.adapterId = p.sourceInfo.adapterId;
                                                    sourceName.header.id = p.sourceInfo.id;
                                                    if (DisplayConfigGetDeviceInfo(&sourceName.header) == ERROR_SUCCESS)
                                                    {
                                                           // find the matched device which is associated with current device 
                                                           // there may be the possibility that display may be duplicated and windows may be one of them in such scenario
                                                           // there may be two callback because source is same target will be different
                                                           // as window is on both the display so either selecting either one is ok
                                                           if (wcscmp(info.szDevice, sourceName.viewGdiDeviceName) == 0) {
                                                                  // get the refresh rate
                                                                  UINT numerator = p.targetInfo.refreshRate.Numerator;
                                                                  UINT denominator = p.targetInfo.refreshRate.Denominator;
                                                                  double refrate = (double)numerator / (double)denominator;
                                                                  *outRefreshRate = refrate;
                                                                  break;
                                                           }
                                                    }
                                             }
                                      }
                                      else
                                      {
                                             hr = E_FAIL;
                                      }
                               }
                               else
                               {
                                      hr = E_FAIL;
                               }
                        }
                 }
          }
   }
   return hr;

}

关于CCD API的更多细节,您可以参考以下链接:

https://docs.microsoft.com/en-us/windows-hardware/drivers/display/ccd-apis

于 2020-05-28T03:58:14.290 回答