Qt: Recreate new window immediately when switching APIs

This commit is contained in:
Stenzek 2023-04-08 13:20:41 +10:00 committed by lightningterror
parent 7f24a5cf82
commit 3cae728aba
10 changed files with 49 additions and 52 deletions

View File

@ -254,13 +254,14 @@ void Host::SetRelativeMouseMode(bool enabled)
{ {
} }
std::optional<WindowInfo> Host::AcquireRenderWindow(RenderAPI api) std::optional<WindowInfo> Host::AcquireRenderWindow()
{ {
return GSRunner::GetPlatformWindowInfo(); return GSRunner::GetPlatformWindowInfo();
} }
std::optional<WindowInfo> Host::UpdateRenderWindow() std::optional<WindowInfo> Host::UpdateRenderWindow(bool recreate_window)
{ {
// We shouldn't ever recreate with the runner, so this is okay..
return GSRunner::GetPlatformWindowInfo(); return GSRunner::GetPlatformWindowInfo();
} }

View File

@ -2233,23 +2233,24 @@ std::optional<WindowInfo> MainWindow::createDisplayWindow(bool fullscreen, bool
return wi; return wi;
} }
std::optional<WindowInfo> MainWindow::updateDisplayWindow(bool fullscreen, bool render_to_main, bool surfaceless) std::optional<WindowInfo> MainWindow::updateDisplayWindow(bool recreate_window, bool fullscreen, bool render_to_main, bool surfaceless)
{ {
DevCon.WriteLn("updateDisplayWindow() fullscreen=%s render_to_main=%s surfaceless=%s", fullscreen ? "true" : "false", DevCon.WriteLn("updateDisplayWindow() recreate=%s fullscreen=%s render_to_main=%s surfaceless=%s", recreate_window ? "true" : "false",
render_to_main ? "true" : "false", surfaceless ? "true" : "false"); fullscreen ? "true" : "false", render_to_main ? "true" : "false", surfaceless ? "true" : "false");
QWidget* container = m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget); QWidget* container = m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget);
const bool is_fullscreen = isRenderingFullscreen(); const bool is_fullscreen = isRenderingFullscreen();
const bool is_rendering_to_main = isRenderingToMain(); const bool is_rendering_to_main = isRenderingToMain();
const bool changing_surfaceless = (!m_display_widget != surfaceless); const bool changing_surfaceless = (!m_display_widget != surfaceless);
if (fullscreen == is_fullscreen && is_rendering_to_main == render_to_main && !changing_surfaceless) if (!recreate_window && fullscreen == is_fullscreen && is_rendering_to_main == render_to_main && !changing_surfaceless)
return m_display_widget->getWindowInfo(); return m_display_widget->getWindowInfo();
// Skip recreating the surface if we're just transitioning between fullscreen and windowed with render-to-main off. // Skip recreating the surface if we're just transitioning between fullscreen and windowed with render-to-main off.
// .. except on Wayland, where everything tends to break if you don't recreate. // .. except on Wayland, where everything tends to break if you don't recreate.
const bool has_container = (m_display_container != nullptr); const bool has_container = (m_display_container != nullptr);
const bool needs_container = DisplayContainer::isNeeded(fullscreen, render_to_main); const bool needs_container = DisplayContainer::isNeeded(fullscreen, render_to_main);
if (!is_rendering_to_main && !render_to_main && has_container == needs_container && !needs_container && !changing_surfaceless) if (!recreate_window && !is_rendering_to_main && !render_to_main && has_container == needs_container && !needs_container &&
!changing_surfaceless)
{ {
DevCon.WriteLn("Toggling to %s without recreating surface", (fullscreen ? "fullscreen" : "windowed")); DevCon.WriteLn("Toggling to %s without recreating surface", (fullscreen ? "fullscreen" : "windowed"));

View File

@ -129,7 +129,7 @@ private Q_SLOTS:
void onUpdateCheckComplete(); void onUpdateCheckComplete();
std::optional<WindowInfo> createDisplayWindow(bool fullscreen, bool render_to_main); std::optional<WindowInfo> createDisplayWindow(bool fullscreen, bool render_to_main);
std::optional<WindowInfo> updateDisplayWindow(bool fullscreen, bool render_to_main, bool surfaceless); std::optional<WindowInfo> updateDisplayWindow(bool recreate_window, bool fullscreen, bool render_to_main, bool surfaceless);
void displayResizeRequested(qint32 width, qint32 height); void displayResizeRequested(qint32 width, qint32 height);
void relativeMouseModeRequested(bool enabled); void relativeMouseModeRequested(bool enabled);
void destroyDisplay(); void destroyDisplay();

View File

@ -901,9 +901,9 @@ std::optional<WindowInfo> EmuThread::acquireRenderWindow()
return emit onCreateDisplayRequested(m_is_fullscreen, m_is_rendering_to_main); return emit onCreateDisplayRequested(m_is_fullscreen, m_is_rendering_to_main);
} }
std::optional<WindowInfo> EmuThread::updateRenderWindow() std::optional<WindowInfo> EmuThread::updateRenderWindow(bool recreate_window)
{ {
return emit onUpdateDisplayRequested(m_is_fullscreen, !m_is_fullscreen && m_is_rendering_to_main, m_is_surfaceless); return emit onUpdateDisplayRequested(recreate_window, m_is_fullscreen, !m_is_fullscreen && m_is_rendering_to_main, m_is_surfaceless);
} }
void EmuThread::releaseRenderWindow() void EmuThread::releaseRenderWindow()
@ -911,14 +911,14 @@ void EmuThread::releaseRenderWindow()
emit onDestroyDisplayRequested(); emit onDestroyDisplayRequested();
} }
std::optional<WindowInfo> Host::AcquireRenderWindow(RenderAPI api) std::optional<WindowInfo> Host::AcquireRenderWindow()
{ {
return g_emu_thread->acquireRenderWindow(); return g_emu_thread->acquireRenderWindow();
} }
std::optional<WindowInfo> Host::UpdateRenderWindow() std::optional<WindowInfo> Host::UpdateRenderWindow(bool recreate_window)
{ {
return g_emu_thread->updateRenderWindow(); return g_emu_thread->updateRenderWindow(recreate_window);
} }
void Host::ReleaseRenderWindow() void Host::ReleaseRenderWindow()

View File

@ -70,7 +70,7 @@ public:
/// Called back from the GS thread when the display state changes (e.g. fullscreen, render to main). /// Called back from the GS thread when the display state changes (e.g. fullscreen, render to main).
std::optional<WindowInfo> acquireRenderWindow(); std::optional<WindowInfo> acquireRenderWindow();
std::optional<WindowInfo> updateRenderWindow(); std::optional<WindowInfo> updateRenderWindow(bool recreate_window);
void connectDisplaySignals(DisplayWidget* widget); void connectDisplaySignals(DisplayWidget* widget);
void releaseRenderWindow(); void releaseRenderWindow();
@ -118,7 +118,7 @@ Q_SIGNALS:
bool messageConfirmed(const QString& title, const QString& message); bool messageConfirmed(const QString& title, const QString& message);
std::optional<WindowInfo> onCreateDisplayRequested(bool fullscreen, bool render_to_main); std::optional<WindowInfo> onCreateDisplayRequested(bool fullscreen, bool render_to_main);
std::optional<WindowInfo> onUpdateDisplayRequested(bool fullscreen, bool render_to_main, bool surfaceless); std::optional<WindowInfo> onUpdateDisplayRequested(bool recreate_window, bool fullscreen, bool render_to_main, bool surfaceless);
void onResizeDisplayRequested(qint32 width, qint32 height); void onResizeDisplayRequested(qint32 width, qint32 height);
void onDestroyDisplayRequested(); void onDestroyDisplayRequested();
void onRelativeMouseModeRequested(bool enabled); void onRelativeMouseModeRequested(bool enabled);

View File

@ -73,7 +73,6 @@ static HRESULT s_hr = E_FAIL;
Pcsx2Config::GSOptions GSConfig; Pcsx2Config::GSOptions GSConfig;
static RenderAPI s_render_api;
static u64 s_next_manual_present_time; static u64 s_next_manual_present_time;
int GSinit() int GSinit()
@ -171,18 +170,17 @@ static void UpdateExclusiveFullscreen(bool force_off)
} }
} }
static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail) static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail, bool reopening, bool recreate_window)
{ {
s_render_api = GetAPIForRenderer(renderer); std::optional<WindowInfo> wi = reopening ? Host::UpdateRenderWindow(recreate_window) : Host::AcquireRenderWindow();
std::optional<WindowInfo> wi = Host::AcquireRenderWindow(s_render_api);
if (!wi.has_value()) if (!wi.has_value())
{ {
Console.Error("Failed to acquire render window."); Console.Error("Failed to acquire render window.");
return false; return false;
} }
switch (s_render_api) const RenderAPI new_api = GetAPIForRenderer(renderer);
switch (new_api)
{ {
#ifdef _WIN32 #ifdef _WIN32
case RenderAPI::D3D11: case RenderAPI::D3D11:
@ -199,7 +197,6 @@ static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail)
#endif #endif
#ifdef ENABLE_OPENGL #ifdef ENABLE_OPENGL
case RenderAPI::OpenGL: case RenderAPI::OpenGL:
case RenderAPI::OpenGLES:
g_gs_device = std::make_unique<GSDeviceOGL>(); g_gs_device = std::make_unique<GSDeviceOGL>();
break; break;
#endif #endif
@ -211,7 +208,7 @@ static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail)
#endif #endif
default: default:
Console.Error("Unsupported render API %s", GSDevice::RenderAPIToString(s_render_api)); Console.Error("Unsupported render API %s", GSDevice::RenderAPIToString(new_api));
return false; return false;
} }
@ -239,8 +236,7 @@ static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail)
GSConfig.OsdShowGPU = EmuConfig.GS.OsdShowGPU && g_gs_device->SetGPUTimingEnabled(true); GSConfig.OsdShowGPU = EmuConfig.GS.OsdShowGPU && g_gs_device->SetGPUTimingEnabled(true);
s_render_api = g_gs_device->GetRenderAPI(); Console.WriteLn(Color_StrongGreen, "%s Graphics Driver Info:", GSDevice::RenderAPIToString(new_api));
Console.WriteLn(Color_StrongGreen, "%s Graphics Driver Info:", GSDevice::RenderAPIToString(s_render_api));
Console.Indent().WriteLn(g_gs_device->GetDriverInfo()); Console.Indent().WriteLn(g_gs_device->GetDriverInfo());
// Switch to exclusive fullscreen if enabled. // Switch to exclusive fullscreen if enabled.
@ -250,7 +246,7 @@ static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail)
return true; return true;
} }
static void CloseGSDevice(bool clear_state) static void CloseGSDevice(bool clear_state, bool reopening)
{ {
if (!g_gs_device) if (!g_gs_device)
return; return;
@ -260,7 +256,8 @@ static void CloseGSDevice(bool clear_state)
g_gs_device->Destroy(); g_gs_device->Destroy();
g_gs_device.reset(); g_gs_device.reset();
Host::ReleaseRenderWindow(); if (!reopening)
Host::ReleaseRenderWindow();
} }
static bool OpenGSRenderer(GSRendererType renderer, u8* basemem) static bool OpenGSRenderer(GSRendererType renderer, u8* basemem)
@ -337,19 +334,24 @@ bool GSreopen(bool recreate_device, bool recreate_renderer, const Pcsx2Config::G
if (recreate_device) if (recreate_device)
{ {
CloseGSDevice(false); // We need a new render window when changing APIs.
const bool recreate_window = (g_gs_device->GetRenderAPI() != GetAPIForRenderer(GSConfig.Renderer));
CloseGSDevice(false, true);
if (!OpenGSDevice(GSConfig.Renderer, false) || (recreate_renderer && !OpenGSRenderer(GSConfig.Renderer, basemem))) if (!OpenGSDevice(GSConfig.Renderer, false, true, recreate_window) ||
(recreate_renderer && !OpenGSRenderer(GSConfig.Renderer, basemem)))
{ {
Host::AddKeyedOSDMessage( Host::AddKeyedOSDMessage(
"GSReopenFailed", "Failed to reopen, restoring old configuration.", Host::OSD_CRITICAL_ERROR_DURATION); "GSReopenFailed", "Failed to reopen, restoring old configuration.", Host::OSD_CRITICAL_ERROR_DURATION);
CloseGSDevice(false); CloseGSDevice(false, true);
GSConfig = old_config; GSConfig = old_config;
if (!OpenGSDevice(GSConfig.Renderer, false) || (recreate_renderer && !OpenGSRenderer(GSConfig.Renderer, basemem))) if (!OpenGSDevice(GSConfig.Renderer, false, true, recreate_window) ||
(recreate_renderer && !OpenGSRenderer(GSConfig.Renderer, basemem)))
{ {
pxFailRel("Failed to reopen GS on old config"); pxFailRel("Failed to reopen GS on old config");
Host::ReleaseRenderWindow();
return false; return false;
} }
} }
@ -385,12 +387,12 @@ bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* b
GSConfig = config; GSConfig = config;
GSConfig.Renderer = renderer; GSConfig.Renderer = renderer;
bool res = OpenGSDevice(renderer, true); bool res = OpenGSDevice(renderer, true, false, false);
if (res) if (res)
{ {
res = OpenGSRenderer(renderer, basemem); res = OpenGSRenderer(renderer, basemem);
if (!res) if (!res)
CloseGSDevice(true); CloseGSDevice(true, false);
} }
if (!res) if (!res)
@ -408,7 +410,7 @@ bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* b
void GSclose() void GSclose()
{ {
CloseGSRenderer(); CloseGSRenderer();
CloseGSDevice(true); CloseGSDevice(true, false);
} }
void GSreset(bool hardware_reset) void GSreset(bool hardware_reset)
@ -627,7 +629,7 @@ void GSUpdateDisplayWindow()
UpdateExclusiveFullscreen(true); UpdateExclusiveFullscreen(true);
g_gs_device->DestroySurface(); g_gs_device->DestroySurface();
const std::optional<WindowInfo> wi = Host::UpdateRenderWindow(); const std::optional<WindowInfo> wi = Host::UpdateRenderWindow(false);
if (!wi.has_value()) if (!wi.has_value())
{ {
pxFailRel("Failed to get window info after update."); pxFailRel("Failed to get window info after update.");
@ -726,7 +728,7 @@ void GSgetInternalResolution(int* width, int* height)
void GSgetStats(std::string& info) void GSgetStats(std::string& info)
{ {
GSPerfMon& pm = g_perfmon; GSPerfMon& pm = g_perfmon;
const char* api_name = GSDevice::RenderAPIToString(s_render_api); const char* api_name = GSDevice::RenderAPIToString(g_gs_device->GetRenderAPI());
if (GSConfig.Renderer == GSRendererType::SW) if (GSConfig.Renderer == GSRendererType::SW)
{ {
const double fps = GetVerticalFrequency(); const double fps = GetVerticalFrequency();
@ -793,7 +795,7 @@ void GSgetTitleStats(std::string& info)
static constexpr const char* deinterlace_modes[] = { static constexpr const char* deinterlace_modes[] = {
"Automatic", "None", "Weave tff", "Weave bff", "Bob tff", "Bob bff", "Blend tff", "Blend bff", "Adaptive tff", "Adaptive bff"}; "Automatic", "None", "Weave tff", "Weave bff", "Bob tff", "Bob bff", "Blend tff", "Blend bff", "Adaptive tff", "Adaptive bff"};
const char* api_name = GSDevice::RenderAPIToString(s_render_api); const char* api_name = GSDevice::RenderAPIToString(g_gs_device->GetRenderAPI());
const char* hw_sw_name = (GSConfig.Renderer == GSRendererType::Null) ? " Null" : (GSConfig.UseHardwareRenderer() ? " HW" : " SW"); const char* hw_sw_name = (GSConfig.Renderer == GSRendererType::Null) ? " Null" : (GSConfig.UseHardwareRenderer() ? " HW" : " SW");
const char* deinterlace_mode = deinterlace_modes[static_cast<int>(GSConfig.InterlaceMode)]; const char* deinterlace_mode = deinterlace_modes[static_cast<int>(GSConfig.InterlaceMode)];
@ -900,15 +902,10 @@ void GSSwitchRenderer(GSRendererType new_renderer)
if (!g_gs_renderer || GSConfig.Renderer == new_renderer) if (!g_gs_renderer || GSConfig.Renderer == new_renderer)
return; return;
RenderAPI existing_api = g_gs_device->GetRenderAPI();
if (existing_api == RenderAPI::OpenGLES)
existing_api = RenderAPI::OpenGL;
const bool is_software_switch = (new_renderer == GSRendererType::SW || GSConfig.Renderer == GSRendererType::SW); const bool is_software_switch = (new_renderer == GSRendererType::SW || GSConfig.Renderer == GSRendererType::SW);
const bool recreate_display = (!is_software_switch && existing_api != GetAPIForRenderer(new_renderer));
const Pcsx2Config::GSOptions old_config(GSConfig); const Pcsx2Config::GSOptions old_config(GSConfig);
GSConfig.Renderer = new_renderer; GSConfig.Renderer = new_renderer;
if (!GSreopen(recreate_display, true, old_config)) if (!GSreopen(!is_software_switch, true, old_config))
pxFailRel("Failed to reopen GS for renderer switch."); pxFailRel("Failed to reopen GS for renderer switch.");
} }

View File

@ -32,8 +32,7 @@ enum class RenderAPI
Metal, Metal,
D3D12, D3D12,
Vulkan, Vulkan,
OpenGL, OpenGL
OpenGLES
}; };
// ST_WRITE is defined in libc, avoid this // ST_WRITE is defined in libc, avoid this
@ -129,11 +128,11 @@ struct GSRecoverableError : GSError
namespace Host namespace Host
{ {
/// Called when the GS is creating a render device. /// Called when the GS is creating a render device.
std::optional<WindowInfo> AcquireRenderWindow(RenderAPI api); std::optional<WindowInfo> AcquireRenderWindow();
/// Called on the MTGS thread when a request to update the display is received. /// Called on the MTGS thread when a request to update the display is received.
/// This could be a fullscreen transition, for example. /// This could be a fullscreen transition, for example.
std::optional<WindowInfo> UpdateRenderWindow(); std::optional<WindowInfo> UpdateRenderWindow(bool recreate_window);
/// Called before drawing the OSD and other display elements. /// Called before drawing the OSD and other display elements.
void BeginPresentFrame(); void BeginPresentFrame();

View File

@ -105,7 +105,6 @@ const char* GSDevice::RenderAPIToString(RenderAPI api)
CASE(Metal); CASE(Metal);
CASE(Vulkan); CASE(Vulkan);
CASE(OpenGL); CASE(OpenGL);
CASE(OpenGLES);
#undef CASE #undef CASE
// clang-format on // clang-format on
default: default:
@ -323,7 +322,7 @@ void GSDevice::Recycle(GSTexture* t)
bool GSDevice::UsesLowerLeftOrigin() const bool GSDevice::UsesLowerLeftOrigin() const
{ {
const RenderAPI api = GetRenderAPI(); const RenderAPI api = GetRenderAPI();
return (api == RenderAPI::OpenGL || api == RenderAPI::OpenGLES); return (api == RenderAPI::OpenGL);
} }
void GSDevice::AgePool() void GSDevice::AgePool()

View File

@ -60,7 +60,7 @@ GSTexture* GSDeviceOGL::CreateSurface(GSTexture::Type type, int width, int heigh
RenderAPI GSDeviceOGL::GetRenderAPI() const RenderAPI GSDeviceOGL::GetRenderAPI() const
{ {
return m_gl_context->IsGLES() ? RenderAPI::OpenGLES : RenderAPI::OpenGL; return RenderAPI::OpenGL;
} }
bool GSDeviceOGL::HasSurface() const bool GSDeviceOGL::HasSurface() const

View File

@ -105,12 +105,12 @@ void Host::SetRelativeMouseMode(bool enabled)
{ {
} }
std::optional<WindowInfo> Host::AcquireRenderWindow(RenderAPI api) std::optional<WindowInfo> Host::AcquireRenderWindow()
{ {
return std::nullopt; return std::nullopt;
} }
std::optional<WindowInfo> Host::UpdateRenderWindow() std::optional<WindowInfo> Host::UpdateRenderWindow(bool recreate_window)
{ {
return std::nullopt; return std::nullopt;
} }