GPUThread: Switch to borderless if exclusive fullscreen fails
Better than ending up windowed.
This commit is contained in:
parent
e36dbaf255
commit
231ba050a2
|
@ -716,7 +716,7 @@ void GameDatabase::Entry::ApplySettings(Settings& settings, bool display_osd_mes
|
|||
"GameDBCompatibility", ICON_EMOJI_INFORMATION,
|
||||
fmt::format("{}{}", TRANSLATE_SV("GameDatabase", "Compatibility settings for this game have been applied."),
|
||||
messages.view()),
|
||||
Host::OSD_WARNING_DURATION);
|
||||
Host::OSD_INFO_DURATION);
|
||||
}
|
||||
|
||||
#undef APPEND_MESSAGE_FMT
|
||||
|
|
|
@ -74,8 +74,9 @@ static bool SleepGPUThread(bool allow_sleep);
|
|||
static bool CreateDeviceOnThread(RenderAPI api, bool fullscreen, bool clear_fsui_state_on_failure, Error* error);
|
||||
static void DestroyDeviceOnThread(bool clear_fsui_state);
|
||||
static void ResizeDisplayWindowOnThread(u32 width, u32 height, float scale);
|
||||
static void UpdateDisplayWindowOnThread(bool fullscreen);
|
||||
static void UpdateDisplayWindowOnThread(bool fullscreen, bool allow_exclusive_fullscreen);
|
||||
static void DisplayWindowResizedOnThread();
|
||||
static bool CheckExclusiveFullscreenOnThread();
|
||||
|
||||
static void ReconfigureOnThread(GPUThreadReconfigureCommand* cmd);
|
||||
static bool CreateGPUBackendOnThread(GPURenderer renderer, bool upload_vram, Error* error);
|
||||
|
@ -725,6 +726,11 @@ bool GPUThread::CreateDeviceOnThread(RenderAPI api, bool fullscreen, bool clear_
|
|||
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
UpdateRunIdle();
|
||||
|
||||
// Switch to borderless if exclusive failed.
|
||||
if (fullscreen_mode.has_value() && !CheckExclusiveFullscreenOnThread())
|
||||
UpdateDisplayWindowOnThread(true, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1145,7 +1151,7 @@ void GPUThread::ResizeDisplayWindowOnThread(u32 width, u32 height, float scale)
|
|||
if (!g_gpu_device->GetMainSwapChain()->ResizeBuffers(width, height, scale, &error))
|
||||
{
|
||||
ERROR_LOG("Failed to resize main swap chain: {}", error.GetDescription());
|
||||
UpdateDisplayWindowOnThread(Host::IsFullscreen());
|
||||
UpdateDisplayWindowOnThread(Host::IsFullscreen(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1154,20 +1160,22 @@ void GPUThread::ResizeDisplayWindowOnThread(u32 width, u32 height, float scale)
|
|||
|
||||
void GPUThread::UpdateDisplayWindow(bool fullscreen)
|
||||
{
|
||||
RunOnThread([fullscreen]() { UpdateDisplayWindowOnThread(fullscreen); });
|
||||
RunOnThread([fullscreen]() { UpdateDisplayWindowOnThread(fullscreen, true); });
|
||||
}
|
||||
|
||||
void GPUThread::UpdateDisplayWindowOnThread(bool fullscreen)
|
||||
void GPUThread::UpdateDisplayWindowOnThread(bool fullscreen, bool allow_exclusive_fullscreen)
|
||||
{
|
||||
// In case we get the event late.
|
||||
if (!g_gpu_device)
|
||||
return;
|
||||
|
||||
bool exclusive_fullscreen_requested = false;
|
||||
std::optional<GPUDevice::ExclusiveFullscreenMode> fullscreen_mode;
|
||||
if (fullscreen && g_gpu_device->GetFeatures().exclusive_fullscreen)
|
||||
if (allow_exclusive_fullscreen && fullscreen && g_gpu_device->GetFeatures().exclusive_fullscreen)
|
||||
{
|
||||
fullscreen_mode =
|
||||
GPUDevice::ExclusiveFullscreenMode::Parse(Host::GetTinyStringSettingValue("GPU", "FullscreenMode", ""));
|
||||
exclusive_fullscreen_requested = fullscreen_mode.has_value();
|
||||
}
|
||||
std::optional<bool> exclusive_fullscreen_control;
|
||||
if (g_settings.display_exclusive_fullscreen_control != DisplayExclusiveFullscreenControl::Automatic)
|
||||
|
@ -1180,7 +1188,7 @@ void GPUThread::UpdateDisplayWindowOnThread(bool fullscreen)
|
|||
|
||||
Error error;
|
||||
std::optional<WindowInfo> wi =
|
||||
Host::AcquireRenderWindow(g_gpu_device->GetRenderAPI(), fullscreen, fullscreen_mode.has_value(), &error);
|
||||
Host::AcquireRenderWindow(g_gpu_device->GetRenderAPI(), fullscreen, exclusive_fullscreen_requested, &error);
|
||||
if (!wi.has_value())
|
||||
{
|
||||
Host::ReportFatalError("Failed to get render window after update", error.GetDescription());
|
||||
|
@ -1205,9 +1213,28 @@ void GPUThread::UpdateDisplayWindowOnThread(bool fullscreen)
|
|||
ERROR_LOG("Failed to switch to surfaceless, rendering commands may fail: {}", error.GetDescription());
|
||||
}
|
||||
|
||||
// If exclusive fullscreen failed, switch to borderless fullscreen.
|
||||
if (exclusive_fullscreen_requested && !CheckExclusiveFullscreenOnThread())
|
||||
{
|
||||
UpdateDisplayWindowOnThread(true, false);
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayWindowResizedOnThread();
|
||||
}
|
||||
|
||||
bool GPUThread::CheckExclusiveFullscreenOnThread()
|
||||
{
|
||||
if (g_gpu_device->HasMainSwapChain() && g_gpu_device->GetMainSwapChain()->IsExclusiveFullscreen())
|
||||
return true;
|
||||
|
||||
Host::AddIconOSDWarning(
|
||||
"ExclusiveFullscreenFailed", ICON_EMOJI_WARNING,
|
||||
TRANSLATE_STR("OSDMessage", "Failed to switch to exclusive fullscreen, using borderless instead."),
|
||||
Host::OSD_INFO_DURATION);
|
||||
return false;
|
||||
}
|
||||
|
||||
void GPUThread::DisplayWindowResizedOnThread()
|
||||
{
|
||||
const GPUSwapChain* swap_chain = g_gpu_device->GetMainSwapChain();
|
||||
|
|
|
@ -233,8 +233,23 @@ bool D3D11SwapChain::InitializeExclusiveFullscreenMode(const GPUDevice::Exclusiv
|
|||
RECT client_rc{};
|
||||
GetClientRect(window_hwnd, &client_rc);
|
||||
|
||||
// Little bit messy...
|
||||
HRESULT hr;
|
||||
ComPtr<IDXGIDevice> dxgi_dev;
|
||||
if (FAILED((hr = D3D11Device::GetD3DDevice()->QueryInterface(IID_PPV_ARGS(dxgi_dev.GetAddressOf())))))
|
||||
{
|
||||
ERROR_LOG("Failed to get DXGIDevice from D3D device: {:08X}", static_cast<unsigned>(hr));
|
||||
return false;
|
||||
}
|
||||
ComPtr<IDXGIAdapter> dxgi_adapter;
|
||||
if (FAILED((hr = dxgi_dev->GetAdapter(dxgi_adapter.GetAddressOf()))))
|
||||
{
|
||||
ERROR_LOG("Failed to get DXGIAdapter from DXGIDevice: {:08X}", static_cast<unsigned>(hr));
|
||||
return false;
|
||||
}
|
||||
|
||||
m_fullscreen_mode = D3DCommon::GetRequestedExclusiveFullscreenModeDesc(
|
||||
D3D11Device::GetDXGIFactory(), client_rc, mode, fm.resource_format, m_fullscreen_output.GetAddressOf());
|
||||
dxgi_adapter.Get(), client_rc, mode, fm.resource_format, m_fullscreen_output.GetAddressOf());
|
||||
return m_fullscreen_mode.has_value();
|
||||
}
|
||||
|
||||
|
@ -444,6 +459,11 @@ bool D3D11SwapChain::SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle
|
|||
return CreateSwapChain(error) && CreateRTV(error);
|
||||
}
|
||||
|
||||
bool D3D11SwapChain::IsExclusiveFullscreen() const
|
||||
{
|
||||
return m_fullscreen_mode.has_value();
|
||||
}
|
||||
|
||||
std::unique_ptr<GPUSwapChain> D3D11Device::CreateSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode,
|
||||
bool allow_present_throttle,
|
||||
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
|
||||
|
|
|
@ -224,10 +224,10 @@ public:
|
|||
ALWAYS_INLINE ID3D11RenderTargetView* GetRTV() const { return m_swap_chain_rtv.Get(); }
|
||||
ALWAYS_INLINE ID3D11RenderTargetView* const* GetRTVArray() const { return m_swap_chain_rtv.GetAddressOf(); }
|
||||
ALWAYS_INLINE bool IsUsingAllowTearing() const { return m_using_allow_tearing; }
|
||||
ALWAYS_INLINE bool IsExclusiveFullscreen() const { return m_fullscreen_mode.has_value(); }
|
||||
|
||||
bool ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error) override;
|
||||
bool SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle, Error* error) override;
|
||||
bool IsExclusiveFullscreen() const override;
|
||||
|
||||
private:
|
||||
static u32 GetNewBufferCount(GPUVSyncMode vsync_mode);
|
||||
|
|
|
@ -880,9 +880,8 @@ bool D3D12SwapChain::InitializeExclusiveFullscreenMode(const GPUDevice::Exclusiv
|
|||
RECT client_rc{};
|
||||
GetClientRect(window_hwnd, &client_rc);
|
||||
|
||||
m_fullscreen_mode =
|
||||
D3DCommon::GetRequestedExclusiveFullscreenModeDesc(D3D12Device::GetInstance().GetDXGIFactory(), client_rc, mode,
|
||||
fm.resource_format, m_fullscreen_output.GetAddressOf());
|
||||
m_fullscreen_mode = D3DCommon::GetRequestedExclusiveFullscreenModeDesc(
|
||||
D3D12Device::GetInstance().GetAdapter(), client_rc, mode, fm.resource_format, m_fullscreen_output.GetAddressOf());
|
||||
return m_fullscreen_mode.has_value();
|
||||
}
|
||||
|
||||
|
@ -1082,6 +1081,11 @@ bool D3D12SwapChain::SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle
|
|||
return CreateSwapChain(dev, error) && CreateRTV(dev, error);
|
||||
}
|
||||
|
||||
bool D3D12SwapChain::IsExclusiveFullscreen() const
|
||||
{
|
||||
return m_fullscreen_mode.has_value();
|
||||
}
|
||||
|
||||
bool D3D12SwapChain::ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error)
|
||||
{
|
||||
m_window_info.surface_scale = new_scale;
|
||||
|
|
|
@ -381,7 +381,6 @@ public:
|
|||
ALWAYS_INLINE IDXGISwapChain1* GetSwapChain() const { return m_swap_chain.Get(); }
|
||||
ALWAYS_INLINE const BufferPair& GetCurrentBuffer() const { return m_swap_chain_buffers[m_current_swap_chain_buffer]; }
|
||||
ALWAYS_INLINE bool IsUsingAllowTearing() const { return m_using_allow_tearing; }
|
||||
ALWAYS_INLINE bool IsExclusiveFullscreen() const { return m_fullscreen_mode.has_value(); }
|
||||
|
||||
void AdvanceBuffer()
|
||||
{
|
||||
|
@ -389,6 +388,7 @@ public:
|
|||
}
|
||||
bool ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error) override;
|
||||
bool SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle, Error* error) override;
|
||||
bool IsExclusiveFullscreen() const override;
|
||||
|
||||
private:
|
||||
static u32 GetNewBufferCount(GPUVSyncMode vsync_mode);
|
||||
|
|
|
@ -225,28 +225,19 @@ GPUDevice::AdapterInfoList D3DCommon::GetAdapterInfoList()
|
|||
}
|
||||
|
||||
std::optional<DXGI_MODE_DESC>
|
||||
D3DCommon::GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect,
|
||||
D3DCommon::GetRequestedExclusiveFullscreenModeDesc(IDXGIAdapter* adapter, const RECT& window_rect,
|
||||
const GPUDevice::ExclusiveFullscreenMode* requested_fullscreen_mode,
|
||||
DXGI_FORMAT format, IDXGIOutput** output)
|
||||
{
|
||||
std::optional<DXGI_MODE_DESC> ret;
|
||||
|
||||
// We need to find which monitor the window is located on.
|
||||
// The adapter must match, you cannot restrict the output to a monitor that is not connected to the device.
|
||||
const GSVector4i client_rc_vec(window_rect.left, window_rect.top, window_rect.right, window_rect.bottom);
|
||||
|
||||
// The window might be on a different adapter to which we are rendering.. so we have to enumerate them all.
|
||||
HRESULT hr;
|
||||
Microsoft::WRL::ComPtr<IDXGIOutput> first_output, intersecting_output;
|
||||
|
||||
for (u32 adapter_index = 0; !intersecting_output; adapter_index++)
|
||||
{
|
||||
Microsoft::WRL::ComPtr<IDXGIAdapter1> adapter;
|
||||
hr = factory->EnumAdapters1(adapter_index, adapter.GetAddressOf());
|
||||
if (hr == DXGI_ERROR_NOT_FOUND)
|
||||
break;
|
||||
else if (FAILED(hr))
|
||||
continue;
|
||||
|
||||
for (u32 output_index = 0;; output_index++)
|
||||
{
|
||||
Microsoft::WRL::ComPtr<IDXGIOutput> this_output;
|
||||
|
@ -269,7 +260,6 @@ D3DCommon::GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const
|
|||
if (!first_output)
|
||||
first_output = std::move(this_output);
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersecting_output)
|
||||
{
|
||||
|
@ -561,11 +551,7 @@ std::optional<DynamicHeapArray<u8>> D3DCommon::CompileShaderWithDXC(u32 shader_m
|
|||
DXC_ARG_OPTIMIZATION_LEVEL3,
|
||||
};
|
||||
static constexpr const wchar_t* debug_arguments[] = {
|
||||
L"-Qstrip_reflect",
|
||||
DXC_ARG_DEBUG,
|
||||
L"-Qembed_debug",
|
||||
DXC_ARG_PACK_MATRIX_ROW_MAJOR,
|
||||
DXC_ARG_SKIP_OPTIMIZATIONS,
|
||||
L"-Qstrip_reflect", DXC_ARG_DEBUG, L"-Qembed_debug", DXC_ARG_PACK_MATRIX_ROW_MAJOR, DXC_ARG_SKIP_OPTIMIZATIONS,
|
||||
};
|
||||
const wchar_t* const* arguments = debug_device ? debug_arguments : nondebug_arguments;
|
||||
const size_t arguments_size = debug_device ? std::size(debug_arguments) : std::size(nondebug_arguments);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <d3dcommon.h>
|
||||
#include <dxgiformat.h>
|
||||
#include <dxgitype.h>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -19,9 +20,9 @@
|
|||
class Error;
|
||||
|
||||
struct IDXGIFactory5;
|
||||
struct IDXGIAdapter;
|
||||
struct IDXGIAdapter1;
|
||||
struct IDXGIOutput;
|
||||
struct DXGI_MODE_DESC;
|
||||
|
||||
namespace D3DCommon {
|
||||
// returns string representation of feature level
|
||||
|
@ -42,7 +43,7 @@ GPUDevice::AdapterInfoList GetAdapterInfoList();
|
|||
|
||||
// returns the fullscreen mode to use for the specified dimensions
|
||||
std::optional<DXGI_MODE_DESC>
|
||||
GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect,
|
||||
GetRequestedExclusiveFullscreenModeDesc(IDXGIAdapter* adapter, const RECT& window_rect,
|
||||
const GPUDevice::ExclusiveFullscreenMode* requested_fullscreen_mode,
|
||||
DXGI_FORMAT format, IDXGIOutput** output);
|
||||
|
||||
|
|
|
@ -275,6 +275,11 @@ GSVector4i GPUSwapChain::PreRotateClipRect(WindowInfo::PreRotation prerotation,
|
|||
return new_clip;
|
||||
}
|
||||
|
||||
bool GPUSwapChain::IsExclusiveFullscreen() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GPUSwapChain::ShouldSkipPresentingFrame()
|
||||
{
|
||||
// Only needed with FIFO. But since we're so fast, we allow it always.
|
||||
|
|
|
@ -528,6 +528,9 @@ public:
|
|||
virtual bool ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error) = 0;
|
||||
virtual bool SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle, Error* error) = 0;
|
||||
|
||||
/// Returns true if exclusive fullscreen is currently active on this swap chain.
|
||||
virtual bool IsExclusiveFullscreen() const;
|
||||
|
||||
bool ShouldSkipPresentingFrame();
|
||||
void ThrottlePresentation();
|
||||
|
||||
|
|
Loading…
Reference in New Issue