[dxgi] Implement display mode changes

Allows games to change the screen resolution in fullscreen
mode. This is currently in a rough shape and some games may
not work as expected when selecting fullscreen mode.
This commit is contained in:
Philip Rebohle 2018-04-29 23:03:27 +02:00
parent a3c561f9dc
commit a32050374c
5 changed files with 170 additions and 22 deletions

View File

@ -79,11 +79,10 @@ namespace dxvk {
if (targetFormat == DXGI_FORMAT_UNKNOWN)
targetFormat = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
UINT targetRefreshRate = 0;
if (pModeToMatch->RefreshRate.Denominator != 0
&& pModeToMatch->RefreshRate.Numerator != 0) {
if (pModeToMatch->RefreshRate.Denominator != 0) {
targetRefreshRate = pModeToMatch->RefreshRate.Numerator
/ pModeToMatch->RefreshRate.Denominator;
}
@ -129,7 +128,7 @@ namespace dxvk {
UINT currDifference = std::abs(int(pModeToMatch->Width - mode.Width))
+ std::abs(int(pModeToMatch->Height - mode.Height));
if (currDifference < minDifference) {
if (currDifference <= minDifference) {
minDifference = currDifference;
*pClosestMatch = mode;
}
@ -326,6 +325,60 @@ namespace dxvk {
}
HRESULT DxgiOutput::GetDisplayMode(DXGI_MODE_DESC* pMode, DWORD ModeNum) {
::MONITORINFOEXW monInfo;
monInfo.cbSize = sizeof(monInfo);
if (!::GetMonitorInfoW(m_monitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {
Logger::err("DXGI: Failed to query monitor info");
return E_FAIL;
}
DEVMODEW devMode = { };
devMode.dmSize = sizeof(devMode);
if (!::EnumDisplaySettingsW(monInfo.szDevice, ModeNum, &devMode))
return DXGI_ERROR_NOT_FOUND;
pMode->Width = devMode.dmPelsWidth;
pMode->Height = devMode.dmPelsHeight;
pMode->RefreshRate = { devMode.dmDisplayFrequency, 1 };
pMode->Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; // FIXME
pMode->ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE;
pMode->Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
return S_OK;
}
HRESULT DxgiOutput::SetDisplayMode(const DXGI_MODE_DESC* pMode) {
::MONITORINFOEXW monInfo;
monInfo.cbSize = sizeof(monInfo);
if (!::GetMonitorInfoW(m_monitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {
Logger::err("DXGI: Failed to query monitor info");
return E_FAIL;
}
DEVMODEW devMode = { };
devMode.dmSize = sizeof(devMode);
devMode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
devMode.dmPelsWidth = pMode->Width;
devMode.dmPelsHeight = pMode->Height;
devMode.dmBitsPerPel = GetFormatBpp(pMode->Format);
if (pMode->RefreshRate.Numerator != 0) {
devMode.dmFields |= DM_DISPLAYFREQUENCY;
devMode.dmDisplayFrequency = pMode->RefreshRate.Numerator
/ pMode->RefreshRate.Denominator;
}
LONG status = ::ChangeDisplaySettingsExW(
monInfo.szDevice, &devMode, nullptr, CDS_FULLSCREEN, nullptr);
return status == DISP_CHANGE_SUCCESSFUL ? S_OK : DXGI_ERROR_NOT_CURRENTLY_AVAILABLE;;
}
uint32_t DxgiOutput::GetFormatBpp(DXGI_FORMAT Format) const {
DXGI_VK_FORMAT_INFO formatInfo = m_adapter->LookupFormat(Format, DXGI_VK_FORMAT_MODE_ANY);
return imageFormatInfo(formatInfo.Format)->elementSize * 8;

View File

@ -77,6 +77,13 @@ namespace dxvk {
HRESULT STDMETHODCALLTYPE WaitForVBlank() final;
HRESULT GetDisplayMode(
DXGI_MODE_DESC* pMode,
DWORD ModeNum);
HRESULT SetDisplayMode(
const DXGI_MODE_DESC* pMode);
private:
Com<DxgiAdapter> m_adapter = nullptr;

View File

@ -60,7 +60,8 @@ namespace dxvk {
DxgiSwapChain::~DxgiSwapChain() {
if (IsWindow(m_desc.OutputWindow) && !m_desc.Windowed)
LeaveFullscreenMode();
}
@ -269,19 +270,49 @@ namespace dxvk {
if (!IsWindow(m_desc.OutputWindow))
return DXGI_ERROR_INVALID_CALL;
// TODO support fullscreen mode
RECT newRect = { 0, 0, 0, 0 };
RECT oldRect = { 0, 0, 0, 0 };
// Update the swap chain description
if (pNewTargetParameters->RefreshRate.Numerator != 0)
m_desc.BufferDesc.RefreshRate = pNewTargetParameters->RefreshRate;
m_desc.BufferDesc.ScanlineOrdering = pNewTargetParameters->ScanlineOrdering;
m_desc.BufferDesc.Scaling = pNewTargetParameters->Scaling;
if (m_desc.Windowed) {
// Adjust window position and size
RECT newRect = { 0, 0, 0, 0 };
RECT oldRect = { 0, 0, 0, 0 };
::GetWindowRect(m_desc.OutputWindow, &oldRect);
::SetRect(&newRect, 0, 0, pNewTargetParameters->Width, pNewTargetParameters->Height);
::AdjustWindowRectEx(&newRect,
::GetWindowLongW(m_desc.OutputWindow, GWL_STYLE), FALSE,
::GetWindowLongW(m_desc.OutputWindow, GWL_EXSTYLE));
::SetRect(&newRect, 0, 0, newRect.right - newRect.left, newRect.bottom - newRect.top);
::OffsetRect(&newRect, oldRect.left, oldRect.top);
::MoveWindow(m_desc.OutputWindow, newRect.left, newRect.top,
newRect.right - newRect.left, newRect.bottom - newRect.top, TRUE);
} else {
Com<IDXGIOutput> output;
if (FAILED(m_adapter->GetOutputFromMonitor(m_monitor, &output))) {
Logger::err("DXGI: ResizeTarget: Failed to query containing output");
return E_FAIL;
}
// If the swap chain allows it, change the display mode
if (m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH)
ChangeDisplayMode(output.ptr(), pNewTargetParameters);
// Resize and reposition the window to
DXGI_OUTPUT_DESC desc;
output->GetDesc(&desc);
const RECT newRect = desc.DesktopCoordinates;
::MoveWindow(m_desc.OutputWindow, newRect.left, newRect.top,
newRect.right - newRect.left, newRect.bottom - newRect.top, TRUE);
}
::GetWindowRect(m_desc.OutputWindow, &oldRect);
::SetRect(&newRect, 0, 0, pNewTargetParameters->Width, pNewTargetParameters->Height);
::AdjustWindowRectEx(&newRect,
::GetWindowLongW(m_desc.OutputWindow, GWL_STYLE), FALSE,
::GetWindowLongW(m_desc.OutputWindow, GWL_EXSTYLE));
::SetRect(&newRect, 0, 0, newRect.right - newRect.left, newRect.bottom - newRect.top);
::OffsetRect(&newRect, oldRect.left, oldRect.top);
::MoveWindow(m_desc.OutputWindow, newRect.left, newRect.top,
newRect.right - newRect.left, newRect.bottom - newRect.top, TRUE);
return S_OK;
}
@ -295,9 +326,6 @@ namespace dxvk {
if (!IsWindow(m_desc.OutputWindow))
return DXGI_ERROR_INVALID_CALL;
if (Fullscreen)
Logger::warn("DxgiSwapChain: Display mode changes not implemented");
if (m_desc.Windowed && Fullscreen)
return this->EnterFullscreenMode(pTarget);
else if (!m_desc.Windowed && !Fullscreen)
@ -404,6 +432,22 @@ namespace dxvk {
}
}
// Find a display mode that matches what we need
::GetWindowRect(m_desc.OutputWindow, &m_windowState.rect);
if (m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH) {
auto windowRect = m_windowState.rect;
DXGI_MODE_DESC displayMode = m_desc.BufferDesc;
displayMode.Width = windowRect.right - windowRect.left;
displayMode.Height = windowRect.bottom - windowRect.top;
if (FAILED(ChangeDisplayMode(output.ptr(), &displayMode))) {
Logger::err("DXGI: EnterFullscreenMode: Failed to change display mode");
return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE;
}
}
// Update swap chain description
m_desc.Windowed = FALSE;
@ -413,7 +457,6 @@ namespace dxvk {
m_windowState.style = style;
m_windowState.exstyle = exstyle;
::GetWindowRect(m_desc.OutputWindow, &m_windowState.rect);
style |= WS_POPUP | WS_SYSMENU;
style &= ~(WS_CAPTION | WS_THICKFRAME);
@ -439,6 +482,13 @@ namespace dxvk {
HRESULT DxgiSwapChain::LeaveFullscreenMode() {
Com<IDXGIOutput> output;
if (FAILED(m_adapter->GetOutputFromMonitor(m_monitor, &output))
|| FAILED(RestoreDisplayMode(output.ptr())))
Logger::warn("DXGI: LeaveFullscreenMode: Failed to restore display mode");
// Restore internal state
m_desc.Windowed = TRUE;
m_monitor = nullptr;
@ -463,6 +513,37 @@ namespace dxvk {
}
HRESULT DxgiSwapChain::ChangeDisplayMode(
IDXGIOutput* pOutput,
const DXGI_MODE_DESC* pDisplayMode) {
auto output = static_cast<DxgiOutput*>(pOutput);
DXGI_MODE_DESC selectedMode;
// Find a close mode that the output supports
HRESULT hr = output->FindClosestMatchingMode(
pDisplayMode, &selectedMode, nullptr);
if (FAILED(hr))
return hr;
return output->SetDisplayMode(&selectedMode);
}
HRESULT DxgiSwapChain::RestoreDisplayMode(IDXGIOutput* pOutput) {
auto output = static_cast<DxgiOutput*>(pOutput);
DXGI_MODE_DESC mode;
HRESULT hr = output->GetDisplayMode(&mode, ENUM_REGISTRY_SETTINGS);
if (FAILED(hr))
return hr;
return output->SetDisplayMode(&mode);
}
HRESULT DxgiSwapChain::GetSampleCount(UINT Count, VkSampleCountFlagBits* pCount) const {
switch (Count) {
case 1: *pCount = VK_SAMPLE_COUNT_1_BIT; return S_OK;

View File

@ -120,6 +120,13 @@ namespace dxvk {
HRESULT LeaveFullscreenMode();
HRESULT ChangeDisplayMode(
IDXGIOutput* pOutput,
const DXGI_MODE_DESC* pDisplayMode);
HRESULT RestoreDisplayMode(
IDXGIOutput* pOutput);
HRESULT GetSampleCount(
UINT Count,
VkSampleCountFlagBits* pCount) const;

View File

@ -91,7 +91,7 @@ public:
swapDesc.OutputWindow = window;
swapDesc.Windowed = true;
swapDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
swapDesc.Flags = 0;
swapDesc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
if (FAILED(m_factory->CreateSwapChain(m_device.ptr(), &swapDesc, &m_swapChain)))
throw DxvkError("Failed to create DXGI swap chain");