Getting display's refresh rate on D3D12
Asked Answered
B

1

8

I am porting my code to D3D12 from D3D11 and I'm trying to obtain display's refresh rate on D3D12. I use the refresh rate for precise animation timing (this is a hard requirement). This code works on 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;
}

Unfortunately, ID3D12Device does not implement IDXGIDevice interface, and FindClosestMatchingMode1 therefore fails with this error:

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

Is there a way to obtain IDXGIDevice when using D3D12? Alternatively, how do I determine display's refresh rate on D3D12?

I know about EnumDisplaySettings however it returns an integer and therefore lacks precision, causing drift in animations. I also found DwmGetCompositionTimingInfo, however, it seems to only support getting info for the main monitor.

I also need a solution that would work on both traditional Win32 and UWP applications. I am open to having to use two code paths for different application models if needed.

Bergess answered 1/5, 2020 at 22:14 Comment(5)
It seems you are developing UWP application (DirectX 12 App (Universal Windows)), right? If yes, you can add UWP tag.Fructidor
I need this code to work in both a traditional Win32 application and a UWP application (although I'm open to having two implementation paths if needed). I looked at the available WinRT API surface and I didn't see anything that would help here so I assume the answer lies somewhere in DXGI API and it would be the same on both application models. I edited my question to clarify.Bergess
I can reproduce this issue. For Direct3D 12, You can via IDXGISwapChain->GetDesc(), then you will get RefreshRate at DXGI_SWAP_CHAIN_DESC -> DXGI_MODE_DESC -> RefreshRate.Fructidor
@RitaHan-MSFT I believe that only works if you set the refresh rate when creating the swap chain in the first place. It does not work in my case. See this: i.imgur.com/CLNY24N.pngBergess
I've consult the related engineer for helping on this issue and will keep you update.Fructidor
A
8

We can get the refresh rate using CCD api, here is the code for your reference:

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://learn.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;

}

More details about CCD API, you can refer the link below:

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

Augusto answered 28/5, 2020 at 3:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.