我正在将代码从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应用程序上运行的解决方案。如果需要,我愿意为不同的应用程序模型使用两个代码路径。
我们可以使用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