GS: Refactor renderer switching

- Fix automatic renderer causing delay when changing settings.
 - Make the Debug -> Switch Renderer menu actually save.
This commit is contained in:
Stenzek 2023-12-31 17:45:13 +10:00 committed by Connor McLaughlin
parent 68df8bf8ea
commit f388de26ab
16 changed files with 148 additions and 145 deletions

View File

@ -250,7 +250,7 @@ void Host::BeginPresentFrame()
GSQueueSnapshot(dump_path); GSQueueSnapshot(dump_path);
} }
if (GSConfig.UseHardwareRenderer()) if (GSIsHardwareRenderer())
{ {
const u32 last_draws = s_total_internal_draws; const u32 last_draws = s_total_internal_draws;
const u32 last_uploads = s_total_uploads; const u32 last_uploads = s_total_uploads;

View File

@ -399,6 +399,8 @@ void MainWindow::connectSignals()
Qt::QueuedConnection); Qt::QueuedConnection);
connect(m_game_list_widget, &GameListWidget::addGameDirectoryRequested, this, connect(m_game_list_widget, &GameListWidget::addGameDirectoryRequested, this,
[this]() { getSettingsWindow()->getGameListSettingsWidget()->addSearchDirectory(this); }); [this]() { getSettingsWindow()->getGameListSettingsWidget()->addSearchDirectory(this); });
createRendererSwitchMenu();
} }
void MainWindow::connectVMThreadSignals(EmuThread* thread) void MainWindow::connectVMThreadSignals(EmuThread* thread)
@ -431,16 +433,48 @@ void MainWindow::connectVMThreadSignals(EmuThread* thread)
connect(m_ui.actionToggleSoftwareRendering, &QAction::triggered, thread, &EmuThread::toggleSoftwareRendering); connect(m_ui.actionToggleSoftwareRendering, &QAction::triggered, thread, &EmuThread::toggleSoftwareRendering);
connect(m_ui.actionDebugger, &QAction::triggered, this, &MainWindow::openDebugger); connect(m_ui.actionDebugger, &QAction::triggered, this, &MainWindow::openDebugger);
connect(m_ui.actionReloadPatches, &QAction::triggered, thread, &EmuThread::reloadPatches); connect(m_ui.actionReloadPatches, &QAction::triggered, thread, &EmuThread::reloadPatches);
}
static constexpr GSRendererType renderers[] = { void MainWindow::createRendererSwitchMenu()
#ifdef _WIN32 {
GSRendererType::DX11, GSRendererType::DX12, static constexpr const GSRendererType renderers[] = {
GSRendererType::Auto,
#if defined(_WIN32)
GSRendererType::DX11,
GSRendererType::DX12,
#elif defined(__APPLE__)
GSRendererType::Metal,
#endif #endif
GSRendererType::OGL, GSRendererType::VK, GSRendererType::SW, GSRendererType::Null}; #ifdef ENABLE_OPENGL
for (GSRendererType renderer : renderers) GSRendererType::OGL,
#endif
#ifdef ENABLE_VULKAN
GSRendererType::VK,
#endif
GSRendererType::SW,
GSRendererType::Null,
};
const GSRendererType current_renderer = static_cast<GSRendererType>(
Host::GetBaseIntSettingValue("EmuCore/GS", "Renderer", static_cast<int>(GSRendererType::Auto)));
for (const GSRendererType renderer : renderers)
{ {
connect(m_ui.menuDebugSwitchRenderer->addAction(QString::fromUtf8(Pcsx2Config::GSOptions::GetRendererName(renderer))), QAction* action = m_ui.menuDebugSwitchRenderer->addAction(
&QAction::triggered, [renderer] { g_emu_thread->switchRenderer(renderer); }); QString::fromUtf8(Pcsx2Config::GSOptions::GetRendererName(renderer)));
action->setCheckable(true);
action->setChecked(current_renderer == renderer);
connect(action,
&QAction::triggered, [this, action, renderer] {
Host::SetBaseIntSettingValue("EmuCore/GS", "Renderer", static_cast<int>(renderer));
Host::CommitBaseSettingChanges();
g_emu_thread->applySettings();
// clear all others
for (QObject* obj : m_ui.menuDebugSwitchRenderer->children())
{
if (QAction* act = qobject_cast<QAction*>(obj); act && act != action)
act->setChecked(false);
}
});
} }
} }

View File

@ -205,6 +205,7 @@ private:
void setupAdditionalUi(); void setupAdditionalUi();
void connectSignals(); void connectSignals();
void createRendererSwitchMenu();
void recreate(); void recreate();
void recreateSettings(); void recreateSettings();
void destroySubWindows(); void destroySubWindows();

View File

@ -610,20 +610,6 @@ void EmuThread::toggleSoftwareRendering()
MTGS::ToggleSoftwareRendering(); MTGS::ToggleSoftwareRendering();
} }
void EmuThread::switchRenderer(GSRendererType renderer)
{
if (!isOnEmuThread())
{
QMetaObject::invokeMethod(this, "switchRenderer", Qt::QueuedConnection, Q_ARG(GSRendererType, renderer));
return;
}
if (!VMManager::HasValidVM())
return;
MTGS::SwitchRenderer(renderer, EmuConfig.GS.InterlaceMode);
}
void EmuThread::changeDisc(CDVD_SourceType source, const QString& path) void EmuThread::changeDisc(CDVD_SourceType source, const QString& path)
{ {
if (!isOnEmuThread()) if (!isOnEmuThread())

View File

@ -92,7 +92,6 @@ public Q_SLOTS:
void reloadGameSettings(); void reloadGameSettings();
void updateEmuFolders(); void updateEmuFolders();
void toggleSoftwareRendering(); void toggleSoftwareRendering();
void switchRenderer(GSRendererType renderer);
void changeDisc(CDVD_SourceType source, const QString& path); void changeDisc(CDVD_SourceType source, const QString& path);
void setELFOverride(const QString& path); void setELFOverride(const QString& path);
void changeGSDump(const QString& path); void changeGSDump(const QString& path);

View File

@ -434,7 +434,6 @@ extern uint eecount_on_last_vdec;
extern bool FMVstarted; extern bool FMVstarted;
extern bool EnableFMV; extern bool EnableFMV;
static bool RendererSwitched = false;
static bool s_last_fmv_state = false; static bool s_last_fmv_state = false;
static __fi void DoFMVSwitch() static __fi void DoFMVSwitch()
@ -479,15 +478,12 @@ static __fi void DoFMVSwitch()
break; break;
} }
if (EmuConfig.Gamefixes.SoftwareRendererFMVHack && (GSConfig.UseHardwareRenderer() || (RendererSwitched && GSConfig.Renderer == GSRendererType::SW))) if (EmuConfig.Gamefixes.SoftwareRendererFMVHack && EmuConfig.GS.UseHardwareRenderer())
{ {
RendererSwitched = GSConfig.UseHardwareRenderer();
DevCon.Warning("FMV Switch"); DevCon.Warning("FMV Switch");
// we don't use the sw toggle here, because it'll change back to auto if set to sw // we don't use the sw toggle here, because it'll change back to auto if set to sw
MTGS::SwitchRenderer(new_fmv_state ? GSRendererType::SW : EmuConfig.GS.Renderer, new_fmv_state ? GSInterlaceMode::AdaptiveTFF : EmuConfig.GS.InterlaceMode, false); MTGS::SetSoftwareRendering(new_fmv_state, new_fmv_state ? GSInterlaceMode::AdaptiveTFF : EmuConfig.GS.InterlaceMode, false);
} }
else
RendererSwitched = false;
} }
static __fi void VSyncStart(u32 sCycle) static __fi void VSyncStart(u32 sCycle)

View File

@ -53,6 +53,8 @@
Pcsx2Config::GSOptions GSConfig; Pcsx2Config::GSOptions GSConfig;
static GSRendererType GSCurrentRenderer;
static u64 s_next_manual_present_time; static u64 s_next_manual_present_time;
void GSinit() void GSinit()
@ -70,6 +72,17 @@ void GSshutdown()
GSJoinSnapshotThreads(); GSJoinSnapshotThreads();
} }
GSRendererType GSGetCurrentRenderer()
{
return GSCurrentRenderer;
}
bool GSIsHardwareRenderer()
{
// Null gets flagged as hw.
return (GSCurrentRenderer != GSRendererType::SW);
}
static RenderAPI GetAPIForRenderer(GSRendererType renderer) static RenderAPI GetAPIForRenderer(GSRendererType renderer)
{ {
switch (renderer) switch (renderer)
@ -93,6 +106,7 @@ static RenderAPI GetAPIForRenderer(GSRendererType renderer)
return RenderAPI::Metal; return RenderAPI::Metal;
#endif #endif
// We could end up here if we ever removed a renderer.
default: default:
return GetAPIForRenderer(GSUtil::GetPreferredRenderer()); return GetAPIForRenderer(GSUtil::GetPreferredRenderer());
} }
@ -191,6 +205,7 @@ static bool OpenGSRenderer(GSRendererType renderer, u8* basemem)
g_gs_renderer->ResetPCRTC(); g_gs_renderer->ResetPCRTC();
g_gs_renderer->UpdateRenderFixes(); g_gs_renderer->UpdateRenderFixes();
g_perfmon.Reset(); g_perfmon.Reset();
GSCurrentRenderer = renderer;
return true; return true;
} }
@ -205,20 +220,18 @@ static void CloseGSRenderer()
} }
} }
bool GSreopen(bool recreate_device, bool recreate_renderer, const Pcsx2Config::GSOptions& old_config) bool GSreopen(bool recreate_device, GSRendererType new_renderer, std::optional<const Pcsx2Config::GSOptions*> old_config)
{ {
Console.WriteLn("Reopening GS with %s device and %s renderer", recreate_device ? "new" : "existing", Console.WriteLn("Reopening GS with %s device", recreate_device ? "new" : "existing");
recreate_renderer ? "new" : "existing");
if (recreate_renderer) g_gs_renderer->Flush(GSState::GSFlushReason::GSREOPEN);
g_gs_renderer->Flush(GSState::GSFlushReason::GSREOPEN);
if (GSConfig.UserHacks_ReadTCOnClose) if (GSConfig.UserHacks_ReadTCOnClose)
g_gs_renderer->ReadbackTextureCache(); g_gs_renderer->ReadbackTextureCache();
std::string capture_filename; std::string capture_filename;
GSVector2i capture_size; GSVector2i capture_size;
if (GSCapture::IsCapturing() && (recreate_renderer || recreate_device)) if (GSCapture::IsCapturing())
{ {
capture_filename = GSCapture::GetNextCaptureFileName(); capture_filename = GSCapture::GetNextCaptureFileName();
capture_size = GSCapture::GetSize(); capture_size = GSCapture::GetSize();
@ -229,40 +242,29 @@ bool GSreopen(bool recreate_device, bool recreate_renderer, const Pcsx2Config::G
u8* basemem = g_gs_renderer->GetRegsMem(); u8* basemem = g_gs_renderer->GetRegsMem();
freezeData fd = {}; freezeData fd = {};
std::unique_ptr<u8[]> fd_data; if (g_gs_renderer->Freeze(&fd, true) != 0)
if (recreate_renderer)
{ {
if (g_gs_renderer->Freeze(&fd, true) != 0) Console.Error("(GSreopen) Failed to get GS freeze size");
{ return false;
Console.Error("(GSreopen) Failed to get GS freeze size");
return false;
}
fd_data = std::make_unique<u8[]>(fd.size);
fd.data = fd_data.get();
if (g_gs_renderer->Freeze(&fd, false) != 0)
{
Console.Error("(GSreopen) Failed to freeze GS");
return false;
}
CloseGSRenderer();
} }
else
std::unique_ptr<u8[]> fd_data = std::make_unique<u8[]>(fd.size);
fd.data = fd_data.get();
if (g_gs_renderer->Freeze(&fd, false) != 0)
{ {
// Make sure nothing is left over. Console.Error("(GSreopen) Failed to freeze GS");
g_gs_renderer->PurgeTextureCache(true, true, true); return false;
g_gs_renderer->PurgePool();
} }
CloseGSRenderer();
if (recreate_device) if (recreate_device)
{ {
// We need a new render window when changing APIs. // We need a new render window when changing APIs.
const bool recreate_window = (g_gs_device->GetRenderAPI() != GetAPIForRenderer(GSConfig.Renderer)); const bool recreate_window = (g_gs_device->GetRenderAPI() != GetAPIForRenderer(GSConfig.Renderer));
CloseGSDevice(false); CloseGSDevice(false);
if (!OpenGSDevice(GSConfig.Renderer, false, recreate_window) || if (!OpenGSDevice(new_renderer, false, recreate_window))
(recreate_renderer && !OpenGSRenderer(GSConfig.Renderer, basemem)))
{ {
Host::AddKeyedOSDMessage("GSReopenFailed", Host::AddKeyedOSDMessage("GSReopenFailed",
TRANSLATE_STR("GS", "Failed to reopen, restoring old configuration."), TRANSLATE_STR("GS", "Failed to reopen, restoring old configuration."),
@ -270,9 +272,10 @@ bool GSreopen(bool recreate_device, bool recreate_renderer, const Pcsx2Config::G
CloseGSDevice(false); CloseGSDevice(false);
GSConfig = old_config; if (old_config.has_value())
if (!OpenGSDevice(GSConfig.Renderer, false, recreate_window) || GSConfig = *old_config.value();
(recreate_renderer && !OpenGSRenderer(GSConfig.Renderer, basemem)))
if (!OpenGSDevice(GSConfig.Renderer, false, recreate_window))
{ {
pxFailRel("Failed to reopen GS on old config"); pxFailRel("Failed to reopen GS on old config");
Host::ReleaseRenderWindow(); Host::ReleaseRenderWindow();
@ -280,22 +283,17 @@ bool GSreopen(bool recreate_device, bool recreate_renderer, const Pcsx2Config::G
} }
} }
} }
else if (recreate_renderer)
if (!OpenGSRenderer(new_renderer, basemem))
{ {
if (!OpenGSRenderer(GSConfig.Renderer, basemem)) Console.Error("(GSreopen) Failed to create new renderer");
{ return false;
Console.Error("(GSreopen) Failed to create new renderer");
return false;
}
} }
if (recreate_renderer) if (g_gs_renderer->Defrost(&fd) != 0)
{ {
if (g_gs_renderer->Defrost(&fd) != 0) Console.Error("(GSreopen) Failed to defrost");
{ return false;
Console.Error("(GSreopen) Failed to defrost");
return false;
}
} }
if (!capture_filename.empty()) if (!capture_filename.empty())
@ -306,12 +304,11 @@ bool GSreopen(bool recreate_device, bool recreate_renderer, const Pcsx2Config::G
bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* basemem) bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* basemem)
{ {
GSConfig = config;
if (renderer == GSRendererType::Auto) if (renderer == GSRendererType::Auto)
renderer = GSUtil::GetPreferredRenderer(); renderer = GSUtil::GetPreferredRenderer();
GSConfig = config;
GSConfig.Renderer = renderer;
bool res = OpenGSDevice(renderer, true, false); bool res = OpenGSDevice(renderer, true, false);
if (res) if (res)
{ {
@ -491,7 +488,7 @@ void GSThrottlePresentation()
void GSGameChanged() void GSGameChanged()
{ {
if (GSConfig.UseHardwareRenderer()) if (GSIsHardwareRenderer())
GSTextureReplacements::GameChanged(); GSTextureReplacements::GameChanged();
} }
@ -610,7 +607,7 @@ void GSgetStats(SmallStringBase& info)
{ {
GSPerfMon& pm = g_perfmon; GSPerfMon& pm = g_perfmon;
const char* api_name = GSDevice::RenderAPIToString(g_gs_device->GetRenderAPI()); const char* api_name = GSDevice::RenderAPIToString(g_gs_device->GetRenderAPI());
if (GSConfig.Renderer == GSRendererType::SW) if (GSCurrentRenderer == GSRendererType::SW)
{ {
const double fps = GetVerticalFrequency(); const double fps = GetVerticalFrequency();
const double fillrate = pm.Get(GSPerfMon::Fillrate); const double fillrate = pm.Get(GSPerfMon::Fillrate);
@ -623,7 +620,7 @@ void GSgetStats(SmallStringBase& info)
pm.Get(GSPerfMon::Unswizzle) / 1024, pm.Get(GSPerfMon::Unswizzle) / 1024,
fps * fillrate / (1024 * 1024)); fps * fillrate / (1024 * 1024));
} }
else if (GSConfig.Renderer == GSRendererType::Null) else if (GSCurrentRenderer == GSRendererType::Null)
{ {
fmt::format_to(std::back_inserter(info), "{} Null", api_name); fmt::format_to(std::back_inserter(info), "{} Null", api_name);
} }
@ -678,7 +675,7 @@ void GSgetTitleStats(std::string& info)
"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(g_gs_device->GetRenderAPI()); 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 = (GSCurrentRenderer == GSRendererType::Null) ? " Null" : (GSIsHardwareRenderer() ? " HW" : " SW");
const char* deinterlace_mode = deinterlace_modes[static_cast<int>(GSConfig.InterlaceMode)]; const char* deinterlace_mode = deinterlace_modes[static_cast<int>(GSConfig.InterlaceMode)];
const char* interlace_mode = ReportInterlaceMode(); const char* interlace_mode = ReportInterlaceMode();
@ -690,11 +687,9 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
{ {
Pcsx2Config::GSOptions old_config(std::move(GSConfig)); Pcsx2Config::GSOptions old_config(std::move(GSConfig));
GSConfig = new_config; GSConfig = new_config;
GSConfig.Renderer = (GSConfig.Renderer == GSRendererType::Auto) ? GSUtil::GetPreferredRenderer() : GSConfig.Renderer;
if (!g_gs_renderer) if (!g_gs_renderer)
return; return;
// Handle OSD scale changes by pushing a window resize through. // Handle OSD scale changes by pushing a window resize through.
if (new_config.OsdScale != old_config.OsdScale) if (new_config.OsdScale != old_config.OsdScale)
ImGuiManager::WindowResized(); ImGuiManager::WindowResized();
@ -702,7 +697,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
// Options which need a full teardown/recreate. // Options which need a full teardown/recreate.
if (!GSConfig.RestartOptionsAreEqual(old_config)) if (!GSConfig.RestartOptionsAreEqual(old_config))
{ {
if (!GSreopen(true, true, old_config)) if (!GSreopen(true, GSConfig.Renderer, &old_config))
pxFailRel("Failed to do full GS reopen"); pxFailRel("Failed to do full GS reopen");
return; return;
} }
@ -711,7 +706,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
if (GSConfig.SWExtraThreads != old_config.SWExtraThreads || if (GSConfig.SWExtraThreads != old_config.SWExtraThreads ||
GSConfig.SWExtraThreadsHeight != old_config.SWExtraThreadsHeight) GSConfig.SWExtraThreadsHeight != old_config.SWExtraThreadsHeight)
{ {
if (!GSreopen(false, true, old_config)) if (!GSreopen(false, GSConfig.Renderer, &old_config))
pxFailRel("Failed to do quick GS reopen"); pxFailRel("Failed to do quick GS reopen");
return; return;
@ -731,7 +726,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
// reload texture cache when trilinear filtering or TC options change // reload texture cache when trilinear filtering or TC options change
if ( if (
(GSConfig.UseHardwareRenderer() && GSConfig.HWMipmap != old_config.HWMipmap) || (GSIsHardwareRenderer() && GSConfig.HWMipmap != old_config.HWMipmap) ||
GSConfig.TexturePreloading != old_config.TexturePreloading || GSConfig.TexturePreloading != old_config.TexturePreloading ||
GSConfig.TriFilter != old_config.TriFilter || GSConfig.TriFilter != old_config.TriFilter ||
GSConfig.GPUPaletteConversion != old_config.GPUPaletteConversion || GSConfig.GPUPaletteConversion != old_config.GPUPaletteConversion ||
@ -755,7 +750,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
g_gs_device->ClearSamplerCache(); g_gs_device->ClearSamplerCache();
// texture dumping/replacement options // texture dumping/replacement options
if (GSConfig.UseHardwareRenderer()) if (GSIsHardwareRenderer())
GSTextureReplacements::UpdateConfig(old_config); GSTextureReplacements::UpdateConfig(old_config);
// clear the hash texture cache since we might have replacements now // clear the hash texture cache since we might have replacements now
@ -773,21 +768,18 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
} }
} }
void GSSwitchRenderer(GSRendererType new_renderer, GSInterlaceMode new_interlace) void GSSetSoftwareRendering(bool software_renderer, GSInterlaceMode new_interlace)
{ {
if (new_renderer == GSRendererType::Auto) if (!g_gs_renderer)
new_renderer = GSUtil::GetPreferredRenderer();
if (!g_gs_renderer || GSConfig.Renderer == new_renderer)
return; return;
const bool is_software_switch = (new_renderer == GSRendererType::SW || GSConfig.Renderer == GSRendererType::SW);
const Pcsx2Config::GSOptions old_config(GSConfig);
GSConfig.Renderer = new_renderer;
GSConfig.InterlaceMode = new_interlace; GSConfig.InterlaceMode = new_interlace;
if (!GSreopen(!is_software_switch, true, old_config)) if (!GSIsHardwareRenderer() != software_renderer)
pxFailRel("Failed to reopen GS for renderer switch."); {
if (!GSreopen(false, software_renderer ? GSRendererType::SW : GSConfig.Renderer, std::nullopt))
pxFailRel("Failed to reopen GS for renderer switch.");
}
} }
bool GSSaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders, bool GSSaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders,

View File

@ -49,9 +49,6 @@ enum class GSDisplayAlignment
RightOrBottom RightOrBottom
}; };
extern Pcsx2Config::GSOptions GSConfig;
class HostDisplay;
class SmallStringBase; class SmallStringBase;
// Returns the ID for the specified function, otherwise -1. // Returns the ID for the specified function, otherwise -1.
@ -62,7 +59,7 @@ s16 GSLookupMoveHandlerFunctionId(const std::string_view& name);
void GSinit(); void GSinit();
void GSshutdown(); void GSshutdown();
bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* basemem); bool GSopen(const Pcsx2Config::GSOptions& config, GSRendererType renderer, u8* basemem);
bool GSreopen(bool recreate_device, bool recreate_renderer, const Pcsx2Config::GSOptions& old_config); bool GSreopen(bool recreate_device, GSRendererType new_renderer, std::optional<const Pcsx2Config::GSOptions*> old_config);
void GSreset(bool hardware_reset); void GSreset(bool hardware_reset);
void GSclose(); void GSclose();
void GSgifSoftReset(u32 mask); void GSgifSoftReset(u32 mask);
@ -90,6 +87,8 @@ void GSResizeDisplayWindow(int width, int height, float scale);
void GSUpdateDisplayWindow(); void GSUpdateDisplayWindow();
void GSSetVSyncMode(VsyncMode mode); void GSSetVSyncMode(VsyncMode mode);
GSRendererType GSGetCurrentRenderer();
bool GSIsHardwareRenderer();
bool GSWantsExclusiveFullscreen(); bool GSWantsExclusiveFullscreen();
bool GSGetHostRefreshRate(float* refresh_rate); bool GSGetHostRefreshRate(float* refresh_rate);
void GSGetAdaptersAndFullscreenModes( void GSGetAdaptersAndFullscreenModes(
@ -105,7 +104,7 @@ void GSgetTitleStats(std::string& info);
void GSTranslateWindowToDisplayCoordinates(float window_x, float window_y, float* display_x, float* display_y); void GSTranslateWindowToDisplayCoordinates(float window_x, float window_y, float* display_x, float* display_y);
void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config); void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config);
void GSSwitchRenderer(GSRendererType new_renderer, GSInterlaceMode new_interlace); void GSSetSoftwareRendering(bool software_renderer, GSInterlaceMode new_interlace);
bool GSSaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders, bool GSSaveSnapshotToMemory(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders,
u32* width, u32* height, std::vector<u32>* pixels); u32* width, u32* height, std::vector<u32>* pixels);
void GSJoinSnapshotThreads(); void GSJoinSnapshotThreads();
@ -135,3 +134,5 @@ namespace Host
void OnCaptureStarted(const std::string& filename); void OnCaptureStarted(const std::string& filename);
void OnCaptureStopped(); void OnCaptureStopped();
} }
extern Pcsx2Config::GSOptions GSConfig;

View File

@ -125,7 +125,7 @@ std::string GSState::GetDrawDumpPath(const char* format, ...)
{ {
std::va_list ap; std::va_list ap;
va_start(ap, format); va_start(ap, format);
const std::string& base = GSConfig.UseHardwareRenderer() ? GSConfig.HWDumpDirectory : GSConfig.SWDumpDirectory; const std::string& base = GSIsHardwareRenderer() ? GSConfig.HWDumpDirectory : GSConfig.SWDumpDirectory;
std::string ret(Path::Combine(base, StringUtil::StdStringFromFormatV(format, ap))); std::string ret(Path::Combine(base, StringUtil::StdStringFromFormatV(format, ap)));
va_end(ap); va_end(ap);
return ret; return ret;
@ -165,7 +165,7 @@ void GSState::Reset(bool hardware_reset)
// bounds value. This means that draws get skipped until the game sets a proper scissor up, which is definitely going to happen // bounds value. This means that draws get skipped until the game sets a proper scissor up, which is definitely going to happen
// after reset (otherwise it'd only ever render 1x1). // after reset (otherwise it'd only ever render 1x1).
// //
if (!hardware_reset && GSConfig.UseHardwareRenderer()) if (!hardware_reset && GSIsHardwareRenderer())
m_env.CTXT[i].scissor.cull = GSVector4i::xffffffff(); m_env.CTXT[i].scissor.cull = GSVector4i::xffffffff();
m_env.CTXT[i].offset.fb = m_mem.GetOffset(m_env.CTXT[i].FRAME.Block(), m_env.CTXT[i].FRAME.FBW, m_env.CTXT[i].FRAME.PSM); m_env.CTXT[i].offset.fb = m_mem.GetOffset(m_env.CTXT[i].FRAME.Block(), m_env.CTXT[i].FRAME.FBW, m_env.CTXT[i].FRAME.PSM);
@ -4421,14 +4421,14 @@ GSVector2i GSState::GSPCRTCRegs::GetFramebufferSize(int display)
if (combined_rect.z >= 2048) if (combined_rect.z >= 2048)
{ {
const int high_x = (PCRTCDisplays[0].framebufferRect.x > PCRTCDisplays[1].framebufferRect.x) ? PCRTCDisplays[0].framebufferRect.x : PCRTCDisplays[1].framebufferRect.x; const int high_x = (PCRTCDisplays[0].framebufferRect.x > PCRTCDisplays[1].framebufferRect.x) ? PCRTCDisplays[0].framebufferRect.x : PCRTCDisplays[1].framebufferRect.x;
combined_rect.z -= GSConfig.UseHardwareRenderer() ? 2048 : high_x; combined_rect.z -= GSIsHardwareRenderer() ? 2048 : high_x;
combined_rect.x = 0; combined_rect.x = 0;
} }
if (combined_rect.w >= 2048) if (combined_rect.w >= 2048)
{ {
const int high_y = (PCRTCDisplays[0].framebufferRect.y > PCRTCDisplays[1].framebufferRect.y) ? PCRTCDisplays[0].framebufferRect.y : PCRTCDisplays[1].framebufferRect.y; const int high_y = (PCRTCDisplays[0].framebufferRect.y > PCRTCDisplays[1].framebufferRect.y) ? PCRTCDisplays[0].framebufferRect.y : PCRTCDisplays[1].framebufferRect.y;
combined_rect.w -= GSConfig.UseHardwareRenderer() ? 2048 : high_y; combined_rect.w -= GSIsHardwareRenderer() ? 2048 : high_y;
combined_rect.y = 0; combined_rect.y = 0;
} }
@ -4442,7 +4442,7 @@ GSVector2i GSState::GSPCRTCRegs::GetFramebufferSize(int display)
} }
// Hardware mode needs a wider framebuffer as it can't offset the read. // Hardware mode needs a wider framebuffer as it can't offset the read.
if (GSConfig.UseHardwareRenderer()) if (GSIsHardwareRenderer())
{ {
combined_rect.z += std::max(PCRTCDisplays[0].framebufferOffsets.x, PCRTCDisplays[1].framebufferOffsets.x); combined_rect.z += std::max(PCRTCDisplays[0].framebufferOffsets.x, PCRTCDisplays[1].framebufferOffsets.x);
combined_rect.w += std::max(PCRTCDisplays[0].framebufferOffsets.y, PCRTCDisplays[1].framebufferOffsets.y); combined_rect.w += std::max(PCRTCDisplays[0].framebufferOffsets.y, PCRTCDisplays[1].framebufferOffsets.y);
@ -4612,7 +4612,7 @@ void GSState::GSPCRTCRegs::RemoveFramebufferOffset(int display)
if (display >= 0) if (display >= 0)
{ {
// Hardware needs nothing but handling for wrapped framebuffers. // Hardware needs nothing but handling for wrapped framebuffers.
if (GSConfig.UseHardwareRenderer()) if (GSIsHardwareRenderer())
{ {
if (PCRTCDisplays[display].framebufferRect.z >= 2048) if (PCRTCDisplays[display].framebufferRect.z >= 2048)
{ {
@ -4648,7 +4648,7 @@ void GSState::GSPCRTCRegs::RemoveFramebufferOffset(int display)
// Software Mode Note: // Software Mode Note:
// This code is to read the framebuffer nicely block aligned in software, then leave the remaining offset in to the block. // This code is to read the framebuffer nicely block aligned in software, then leave the remaining offset in to the block.
// In hardware mode this doesn't happen, it reads the whole framebuffer, so we need to keep the offset. // In hardware mode this doesn't happen, it reads the whole framebuffer, so we need to keep the offset.
if (!GSConfig.UseHardwareRenderer()) if (!GSIsHardwareRenderer())
{ {
const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[PCRTCDisplays[1].PSM]; const GSLocalMemory::psm_t& psm = GSLocalMemory::m_psm[PCRTCDisplays[1].PSM];

View File

@ -195,28 +195,35 @@ u32 GSUtil::GetChannelMask(u32 spsm, u32 fbmsk)
GSRendererType GSUtil::GetPreferredRenderer() GSRendererType GSUtil::GetPreferredRenderer()
{ {
// Memorize the value, so we don't keep re-querying it.
static GSRendererType preferred_renderer = GSRendererType::Auto;
if (preferred_renderer == GSRendererType::Auto)
{
#if defined(__APPLE__) #if defined(__APPLE__)
// Mac: Prefer Metal hardware. // Mac: Prefer Metal hardware.
return GSRendererType::Metal; preferred_renderer = GSRendererType::Metal;
#elif defined(_WIN32) #elif defined(_WIN32)
// Use D3D device info to select renderer. // Use D3D device info to select renderer.
return D3D::GetPreferredRenderer(); preferred_renderer = D3D::GetPreferredRenderer();
#else #else
// Linux: Prefer Vulkan if the driver isn't buggy. // Linux: Prefer Vulkan if the driver isn't buggy.
#if defined(ENABLE_VULKAN) #if defined(ENABLE_VULKAN)
if (GSDeviceVK::IsSuitableDefaultRenderer()) if (GSDeviceVK::IsSuitableDefaultRenderer())
return GSRendererType::VK; preferred_renderer = GSRendererType::VK;
#endif #endif
// Otherwise, whatever is available. // Otherwise, whatever is available.
#if defined(ENABLE_OPENGL) #if defined(ENABLE_OPENGL)
return GSRendererType::OGL; preferred_renderer = GSRendererType::OGL;
#elif defined(ENABLE_VULKAN) #elif defined(ENABLE_VULKAN)
return GSRendererType::VK; preferred_renderer = GSRendererType::VK;
#else #else
return GSRendererType::SW; preferred_renderer = GSRendererType::SW;
#endif #endif
#endif #endif
}
return preferred_renderer;
} }
const char* psm_str(int psm) const char* psm_str(int psm)

View File

@ -511,7 +511,7 @@ bool GSRenderer::BeginPresentFrame(bool frame_skip)
// Device lost, something went really bad. // Device lost, something went really bad.
// Let's just toss out everything, and try to hobble on. // Let's just toss out everything, and try to hobble on.
if (!GSreopen(true, false, GSConfig)) if (!GSreopen(true, GSGetCurrentRenderer(), std::nullopt))
{ {
pxFailRel("Failed to recreate GS device after loss."); pxFailRel("Failed to recreate GS device after loss.");
return false; return false;

View File

@ -4824,7 +4824,7 @@ void FullscreenUI::DrawPauseMenu(MainWindowType type)
ClosePauseMenu(); ClosePauseMenu();
} }
if (ActiveButton(GSConfig.UseHardwareRenderer() ? (FSUI_ICONSTR(ICON_FA_PAINT_BRUSH, "Switch To Software Renderer")) : if (ActiveButton(GSIsHardwareRenderer() ? (FSUI_ICONSTR(ICON_FA_PAINT_BRUSH, "Switch To Software Renderer")) :
(FSUI_ICONSTR(ICON_FA_PAINT_BRUSH, "Switch To Hardware Renderer")), (FSUI_ICONSTR(ICON_FA_PAINT_BRUSH, "Switch To Hardware Renderer")),
false)) false))
{ {

View File

@ -355,7 +355,7 @@ void ImGuiManager::DrawSettingsOverlay()
EmuConfig.EnableNoInterlacingPatches ? "N" : ""); EmuConfig.EnableNoInterlacingPatches ? "N" : "");
} }
if (GSConfig.UseHardwareRenderer()) if (GSIsHardwareRenderer())
{ {
if ((GSConfig.UpscaleMultiplier - std::floor(GSConfig.UpscaleMultiplier)) > 0.01) if ((GSConfig.UpscaleMultiplier - std::floor(GSConfig.UpscaleMultiplier)) > 0.01)
APPEND("IR={:.2f} ", static_cast<float>(GSConfig.UpscaleMultiplier)); APPEND("IR={:.2f} ", static_cast<float>(GSConfig.UpscaleMultiplier));

View File

@ -268,7 +268,7 @@ void MTGS::PostVsyncStart(bool registers_written)
void MTGS::InitAndReadFIFO(u8* mem, u32 qwc) void MTGS::InitAndReadFIFO(u8* mem, u32 qwc)
{ {
if (EmuConfig.GS.HWDownloadMode >= GSHardwareDownloadMode::Unsynchronized && GSConfig.UseHardwareRenderer()) if (EmuConfig.GS.HWDownloadMode >= GSHardwareDownloadMode::Unsynchronized && GSIsHardwareRenderer())
{ {
if (EmuConfig.GS.HWDownloadMode == GSHardwareDownloadMode::Unsynchronized) if (EmuConfig.GS.HWDownloadMode == GSHardwareDownloadMode::Unsynchronized)
GSReadLocalMemoryUnsync(mem, qwc, vif1.BITBLTBUF._u64, vif1.TRXPOS._u64, vif1.TRXREG._u64); GSReadLocalMemoryUnsync(mem, qwc, vif1.BITBLTBUF._u64, vif1.TRXPOS._u64, vif1.TRXREG._u64);
@ -977,18 +977,19 @@ void MTGS::UpdateVSyncMode()
SetVSyncMode(Host::GetEffectiveVSyncMode()); SetVSyncMode(Host::GetEffectiveVSyncMode());
} }
void MTGS::SwitchRenderer(GSRendererType renderer, GSInterlaceMode interlace, bool display_message /* = true */) void MTGS::SetSoftwareRendering(bool software, GSInterlaceMode interlace, bool display_message /* = true */)
{ {
pxAssertRel(IsOpen(), "MTGS is running"); pxAssertRel(IsOpen(), "MTGS is running");
if (display_message) if (display_message)
{ {
Host::AddIconOSDMessage("SwitchRenderer", ICON_FA_MAGIC, fmt::format("Switching to {} renderer...", Host::AddIconOSDMessage("SwitchRenderer", ICON_FA_MAGIC, software ?
Pcsx2Config::GSOptions::GetRendererName(renderer)), Host::OSD_INFO_DURATION); TRANSLATE_STR("GS", "Switching to software renderer...") : TRANSLATE_STR("GS", "Switching to hardware renderer..."),
Host::OSD_QUICK_DURATION);
} }
RunOnGSThread([renderer, interlace]() { RunOnGSThread([software, interlace]() {
GSSwitchRenderer(renderer, interlace); GSSetSoftwareRendering(software, interlace);
}); });
// See note in ApplySettings() for reasoning here. // See note in ApplySettings() for reasoning here.
@ -996,22 +997,10 @@ void MTGS::SwitchRenderer(GSRendererType renderer, GSInterlaceMode interlace, bo
WaitGS(false, false, false); WaitGS(false, false, false);
} }
void MTGS::SetSoftwareRendering(bool software, bool display_message /* = true */)
{
// for hardware, use the chosen api in the base config, or auto if base is set to sw
GSRendererType new_renderer;
if (!software)
new_renderer = EmuConfig.GS.UseHardwareRenderer() ? EmuConfig.GS.Renderer : GSRendererType::Auto;
else
new_renderer = GSRendererType::SW;
SwitchRenderer(new_renderer, EmuConfig.GS.InterlaceMode, display_message);
}
void MTGS::ToggleSoftwareRendering() void MTGS::ToggleSoftwareRendering()
{ {
// reading from the GS thread.. but should be okay here // reading from the GS thread.. but should be okay here
SetSoftwareRendering(GSConfig.Renderer != GSRendererType::SW); SetSoftwareRendering(GSIsHardwareRenderer(), EmuConfig.GS.InterlaceMode);
} }
bool MTGS::SaveMemorySnapshot(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders, bool MTGS::SaveMemorySnapshot(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders,

View File

@ -72,8 +72,7 @@ namespace MTGS
void UpdateDisplayWindow(); void UpdateDisplayWindow();
void SetVSyncMode(VsyncMode mode); void SetVSyncMode(VsyncMode mode);
void UpdateVSyncMode(); void UpdateVSyncMode();
void SwitchRenderer(GSRendererType renderer, GSInterlaceMode interlace, bool display_message = true); void SetSoftwareRendering(bool software, GSInterlaceMode interlace, bool display_message = true);
void SetSoftwareRendering(bool software, bool display_message = true);
void ToggleSoftwareRendering(); void ToggleSoftwareRendering();
bool SaveMemorySnapshot(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders, bool SaveMemorySnapshot(u32 window_width, u32 window_height, bool apply_aspect, bool crop_borders,
u32* width, u32* height, std::vector<u32>* pixels); u32* width, u32* height, std::vector<u32>* pixels);

View File

@ -962,8 +962,7 @@ void Pcsx2Config::GSOptions::LoadSave(SettingsWrapper& wrap)
#undef GSSettingStringEx #undef GSSettingStringEx
// Sanity check: don't dump a bunch of crap in the current working directory. // Sanity check: don't dump a bunch of crap in the current working directory.
const std::string& dump_dir = UseHardwareRenderer() ? HWDumpDirectory : SWDumpDirectory; if (DumpGSData && (HWDumpDirectory.empty() || SWDumpDirectory.empty()))
if (DumpGSData && dump_dir.empty())
{ {
Console.Error("Draw dumping is enabled but directory is unconfigured, please set one."); Console.Error("Draw dumping is enabled but directory is unconfigured, please set one.");
DumpGSData = false; DumpGSData = false;