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();
}
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();
}

View File

@ -2233,23 +2233,24 @@ std::optional<WindowInfo> MainWindow::createDisplayWindow(bool fullscreen, bool
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",
render_to_main ? "true" : "false", surfaceless ? "true" : "false");
DevCon.WriteLn("updateDisplayWindow() recreate=%s fullscreen=%s render_to_main=%s surfaceless=%s", recreate_window ? "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);
const bool is_fullscreen = isRenderingFullscreen();
const bool is_rendering_to_main = isRenderingToMain();
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();
// 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.
const bool has_container = (m_display_container != nullptr);
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"));

View File

@ -129,7 +129,7 @@ private Q_SLOTS:
void onUpdateCheckComplete();
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 relativeMouseModeRequested(bool enabled);
void destroyDisplay();

View File

@ -901,9 +901,9 @@ std::optional<WindowInfo> EmuThread::acquireRenderWindow()
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()
@ -911,14 +911,14 @@ void EmuThread::releaseRenderWindow()
emit onDestroyDisplayRequested();
}
std::optional<WindowInfo> Host::AcquireRenderWindow(RenderAPI api)
std::optional<WindowInfo> Host::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()

View File

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

View File

@ -73,7 +73,6 @@ static HRESULT s_hr = E_FAIL;
Pcsx2Config::GSOptions GSConfig;
static RenderAPI s_render_api;
static u64 s_next_manual_present_time;
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 = Host::AcquireRenderWindow(s_render_api);
std::optional<WindowInfo> wi = reopening ? Host::UpdateRenderWindow(recreate_window) : Host::AcquireRenderWindow();
if (!wi.has_value())
{
Console.Error("Failed to acquire render window.");
return false;
}
switch (s_render_api)
const RenderAPI new_api = GetAPIForRenderer(renderer);
switch (new_api)
{
#ifdef _WIN32
case RenderAPI::D3D11:
@ -199,7 +197,6 @@ static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail)
#endif
#ifdef ENABLE_OPENGL
case RenderAPI::OpenGL:
case RenderAPI::OpenGLES:
g_gs_device = std::make_unique<GSDeviceOGL>();
break;
#endif
@ -211,7 +208,7 @@ static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail)
#endif
default:
Console.Error("Unsupported render API %s", GSDevice::RenderAPIToString(s_render_api));
Console.Error("Unsupported render API %s", GSDevice::RenderAPIToString(new_api));
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);
s_render_api = g_gs_device->GetRenderAPI();
Console.WriteLn(Color_StrongGreen, "%s Graphics Driver Info:", GSDevice::RenderAPIToString(s_render_api));
Console.WriteLn(Color_StrongGreen, "%s Graphics Driver Info:", GSDevice::RenderAPIToString(new_api));
Console.Indent().WriteLn(g_gs_device->GetDriverInfo());
// Switch to exclusive fullscreen if enabled.
@ -250,7 +246,7 @@ static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail)
return true;
}
static void CloseGSDevice(bool clear_state)
static void CloseGSDevice(bool clear_state, bool reopening)
{
if (!g_gs_device)
return;
@ -260,7 +256,8 @@ static void CloseGSDevice(bool clear_state)
g_gs_device->Destroy();
g_gs_device.reset();
Host::ReleaseRenderWindow();
if (!reopening)
Host::ReleaseRenderWindow();
}
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)
{
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(
"GSReopenFailed", "Failed to reopen, restoring old configuration.", Host::OSD_CRITICAL_ERROR_DURATION);
CloseGSDevice(false);
CloseGSDevice(false, true);
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");
Host::ReleaseRenderWindow();
return false;
}
}
@ -385,12 +387,12 @@ bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* b
GSConfig = config;
GSConfig.Renderer = renderer;
bool res = OpenGSDevice(renderer, true);
bool res = OpenGSDevice(renderer, true, false, false);
if (res)
{
res = OpenGSRenderer(renderer, basemem);
if (!res)
CloseGSDevice(true);
CloseGSDevice(true, false);
}
if (!res)
@ -408,7 +410,7 @@ bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* b
void GSclose()
{
CloseGSRenderer();
CloseGSDevice(true);
CloseGSDevice(true, false);
}
void GSreset(bool hardware_reset)
@ -627,7 +629,7 @@ void GSUpdateDisplayWindow()
UpdateExclusiveFullscreen(true);
g_gs_device->DestroySurface();
const std::optional<WindowInfo> wi = Host::UpdateRenderWindow();
const std::optional<WindowInfo> wi = Host::UpdateRenderWindow(false);
if (!wi.has_value())
{
pxFailRel("Failed to get window info after update.");
@ -726,7 +728,7 @@ void GSgetInternalResolution(int* width, int* height)
void GSgetStats(std::string& info)
{
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)
{
const double fps = GetVerticalFrequency();
@ -793,7 +795,7 @@ void GSgetTitleStats(std::string& info)
static constexpr const char* deinterlace_modes[] = {
"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* 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)
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 recreate_display = (!is_software_switch && existing_api != GetAPIForRenderer(new_renderer));
const Pcsx2Config::GSOptions old_config(GSConfig);
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.");
}

View File

@ -32,8 +32,7 @@ enum class RenderAPI
Metal,
D3D12,
Vulkan,
OpenGL,
OpenGLES
OpenGL
};
// ST_WRITE is defined in libc, avoid this
@ -129,11 +128,11 @@ struct GSRecoverableError : GSError
namespace Host
{
/// 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.
/// 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.
void BeginPresentFrame();

View File

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

View File

@ -60,7 +60,7 @@ GSTexture* GSDeviceOGL::CreateSurface(GSTexture::Type type, int width, int heigh
RenderAPI GSDeviceOGL::GetRenderAPI() const
{
return m_gl_context->IsGLES() ? RenderAPI::OpenGLES : RenderAPI::OpenGL;
return RenderAPI::OpenGL;
}
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;
}
std::optional<WindowInfo> Host::UpdateRenderWindow()
std::optional<WindowInfo> Host::UpdateRenderWindow(bool recreate_window)
{
return std::nullopt;
}