mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-02-20 01:54:16 +01:00
[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:
parent
a3c561f9dc
commit
a32050374c
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
Loading…
x
Reference in New Issue
Block a user