GS: Fix use-after-free on lost device

This commit is contained in:
Connor McLaughlin 2024-05-11 21:21:03 +10:00
parent 95843dc84a
commit 4cc4a6561c
4 changed files with 48 additions and 36 deletions

View File

@ -210,14 +210,24 @@ static void CloseGSRenderer()
} }
} }
bool GSreopen(bool recreate_device, GSRendererType new_renderer, std::optional<const Pcsx2Config::GSOptions*> old_config) bool GSreopen(bool recreate_device, bool recreate_renderer, GSRendererType new_renderer,
std::optional<const Pcsx2Config::GSOptions*> old_config)
{ {
Console.WriteLn("Reopening GS with %s device", recreate_device ? "new" : "existing"); Console.WriteLn("Reopening GS with %s device", recreate_device ? "new" : "existing");
g_gs_renderer->Flush(GSState::GSFlushReason::GSREOPEN); g_gs_renderer->Flush(GSState::GSFlushReason::GSREOPEN);
if (GSConfig.UserHacks_ReadTCOnClose) if (recreate_device && !recreate_renderer)
{
// Keeping the renderer around, this probably means we lost the device, so toss everything.
g_gs_renderer->PurgeTextureCache(true, true, true);
g_gs_device->ClearCurrent();
g_gs_device->PurgePool();
}
else 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;
@ -232,21 +242,25 @@ bool GSreopen(bool recreate_device, GSRendererType new_renderer, std::optional<c
u8* basemem = g_gs_renderer->GetRegsMem(); u8* basemem = g_gs_renderer->GetRegsMem();
freezeData fd = {}; freezeData fd = {};
if (g_gs_renderer->Freeze(&fd, true) != 0) std::unique_ptr<u8[]> fd_data;
if (recreate_renderer)
{ {
Console.Error("(GSreopen) Failed to get GS freeze size"); if (g_gs_renderer->Freeze(&fd, true) != 0)
return false; {
} Console.Error("(GSreopen) Failed to get GS freeze size");
return false;
}
std::unique_ptr<u8[]> fd_data = std::make_unique<u8[]>(fd.size); fd_data = std::make_unique<u8[]>(fd.size);
fd.data = fd_data.get(); fd.data = fd_data.get();
if (g_gs_renderer->Freeze(&fd, false) != 0) if (g_gs_renderer->Freeze(&fd, false) != 0)
{ {
Console.Error("(GSreopen) Failed to freeze GS"); Console.Error("(GSreopen) Failed to freeze GS");
return false; return false;
} }
CloseGSRenderer(); CloseGSRenderer();
}
if (recreate_device) if (recreate_device)
{ {
@ -274,16 +288,19 @@ bool GSreopen(bool recreate_device, GSRendererType new_renderer, std::optional<c
} }
} }
if (!OpenGSRenderer(new_renderer, basemem)) if (recreate_renderer)
{ {
Console.Error("(GSreopen) Failed to create new renderer"); if (!OpenGSRenderer(new_renderer, basemem))
return false; {
} Console.Error("(GSreopen) Failed to create new renderer");
return false;
}
if (g_gs_renderer->Defrost(&fd) != 0) if (g_gs_renderer->Defrost(&fd) != 0)
{ {
Console.Error("(GSreopen) Failed to defrost"); Console.Error("(GSreopen) Failed to defrost");
return false; return false;
}
} }
if (!capture_filename.empty()) if (!capture_filename.empty())
@ -693,7 +710,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, GSConfig.Renderer, &old_config)) if (!GSreopen(true, true, GSConfig.Renderer, &old_config))
pxFailRel("Failed to do full GS reopen"); pxFailRel("Failed to do full GS reopen");
return; return;
} }
@ -702,7 +719,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, GSConfig.Renderer, &old_config)) if (!GSreopen(false, true, GSConfig.Renderer, &old_config))
pxFailRel("Failed to do quick GS reopen"); pxFailRel("Failed to do quick GS reopen");
return; return;
@ -738,7 +755,8 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)
if (GSConfig.UserHacks_ReadTCOnClose) if (GSConfig.UserHacks_ReadTCOnClose)
g_gs_renderer->ReadbackTextureCache(); g_gs_renderer->ReadbackTextureCache();
g_gs_renderer->PurgeTextureCache(true, true, true); g_gs_renderer->PurgeTextureCache(true, true, true);
g_gs_renderer->PurgePool(); g_gs_device->ClearCurrent();
g_gs_device->PurgePool();
} }
// clear out the sampler cache when AF options change, since the anisotropy gets baked into them // clear out the sampler cache when AF options change, since the anisotropy gets baked into them
@ -776,7 +794,7 @@ void GSSetSoftwareRendering(bool software_renderer, GSInterlaceMode new_interlac
// Config might be SW, and we're switching to HW -> use Auto. // Config might be SW, and we're switching to HW -> use Auto.
const GSRendererType renderer = (software_renderer ? GSRendererType::SW : const GSRendererType renderer = (software_renderer ? GSRendererType::SW :
(GSConfig.Renderer == GSRendererType::SW ? GSRendererType::Auto : GSConfig.Renderer)); (GSConfig.Renderer == GSRendererType::SW ? GSRendererType::Auto : GSConfig.Renderer));
if (!GSreopen(false, renderer, std::nullopt)) if (!GSreopen(false, true, renderer, std::nullopt))
pxFailRel("Failed to reopen GS for renderer switch."); pxFailRel("Failed to reopen GS for renderer switch.");
} }
} }
@ -1115,7 +1133,7 @@ BEGIN_HOTKEY_LIST(g_gs_hotkeys){"Screenshot", TRANSLATE_NOOP("Hotkeys", "Graphic
MTGS::RunOnGSThread([new_level]() { MTGS::RunOnGSThread([new_level]() {
GSConfig.HWMipmap = new_level; GSConfig.HWMipmap = new_level;
g_gs_renderer->PurgeTextureCache(true, false, true); g_gs_renderer->PurgeTextureCache(true, false, true);
g_gs_renderer->PurgePool(); g_gs_device->PurgePool();
}); });
}}, }},
{"CycleInterlaceMode", TRANSLATE_NOOP("Hotkeys", "Graphics"), TRANSLATE_NOOP("Hotkeys", "Cycle Deinterlace Mode"), {"CycleInterlaceMode", TRANSLATE_NOOP("Hotkeys", "Graphics"), TRANSLATE_NOOP("Hotkeys", "Cycle Deinterlace Mode"),

View File

@ -57,7 +57,8 @@ s16 GSLookupBeforeDrawFunctionId(const std::string_view& name);
s16 GSLookupMoveHandlerFunctionId(const std::string_view& name); s16 GSLookupMoveHandlerFunctionId(const std::string_view& name);
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, GSRendererType new_renderer, std::optional<const Pcsx2Config::GSOptions*> old_config); bool GSreopen(bool recreate_device, bool recreate_renderer, 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);

View File

@ -75,11 +75,6 @@ void GSRenderer::Destroy()
GSCapture::EndCapture(); GSCapture::EndCapture();
} }
void GSRenderer::PurgePool()
{
g_gs_device->PurgePool();
}
void GSRenderer::UpdateRenderFixes() void GSRenderer::UpdateRenderFixes()
{ {
} }
@ -518,7 +513,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, GSGetCurrentRenderer(), std::nullopt)) if (!GSreopen(true, false, 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

@ -44,8 +44,6 @@ public:
virtual void UpdateRenderFixes(); virtual void UpdateRenderFixes();
void PurgePool();
virtual void VSync(u32 field, bool registers_written, bool idle_frame); virtual void VSync(u32 field, bool registers_written, bool idle_frame);
virtual bool CanUpscale() { return false; } virtual bool CanUpscale() { return false; }
virtual float GetUpscaleMultiplier() { return 1.0f; } virtual float GetUpscaleMultiplier() { return 1.0f; }