System: Add fatal error shutdown path

Switch to a null backend and shut down the system instead of crashing.
This commit is contained in:
Stenzek 2025-01-17 20:20:29 +10:00
parent d52bf795e4
commit 6cba825bac
No known key found for this signature in database
17 changed files with 313 additions and 59 deletions

View File

@ -985,7 +985,7 @@ void GPU::UpdateCRTCDisplayParameters()
if ((cs.display_vram_width != old_vram_width || cs.display_vram_height != old_vram_height) && if ((cs.display_vram_width != old_vram_width || cs.display_vram_height != old_vram_height) &&
g_settings.gpu_resolution_scale == 0) g_settings.gpu_resolution_scale == 0)
{ {
GPUThread::RunOnBackend([](GPUBackend* backend) { backend->UpdateResolutionScale(); }, false, false); GPUBackend::QueueUpdateResolutionScale();
} }
} }

View File

@ -125,10 +125,12 @@ bool GPUBackend::Initialize(bool clear_vram, Error* error)
return true; return true;
} }
void GPUBackend::UpdateSettings(const GPUSettings& old_settings) bool GPUBackend::UpdateSettings(const GPUSettings& old_settings, Error* error)
{ {
if (g_gpu_settings.display_show_gpu_stats != old_settings.display_show_gpu_stats) if (g_gpu_settings.display_show_gpu_stats != old_settings.display_show_gpu_stats)
GPUBackend::ResetStatistics(); GPUBackend::ResetStatistics();
return true;
} }
GPUThreadCommand* GPUBackend::NewClearVRAMCommand() GPUThreadCommand* GPUBackend::NewClearVRAMCommand()
@ -369,6 +371,20 @@ bool GPUBackend::AllocateMemorySaveStates(std::span<System::MemorySaveState> sta
return result; return result;
} }
void GPUBackend::QueueUpdateResolutionScale()
{
DebugAssert(!GPUThread::IsOnThread());
GPUThread::RunOnBackend(
[](GPUBackend* backend) {
Error error;
if (!backend->UpdateResolutionScale(&error)) [[unlikely]]
GPUThread::ReportFatalErrorAndShutdown(
fmt::format("Failed to update resolution scale: {}", error.GetDescription()));
},
false, true);
}
void GPUBackend::HandleCommand(const GPUThreadCommand* cmd) void GPUBackend::HandleCommand(const GPUThreadCommand* cmd)
{ {
switch (cmd->type) switch (cmd->type)
@ -753,3 +769,158 @@ void GPUBackend::RenderScreenshotToFile(const std::string_view path, DisplayScre
}, },
false, false); false, false);
} }
namespace {
class GPUNullBackend final : public GPUBackend
{
public:
GPUNullBackend(GPUPresenter& presenter);
~GPUNullBackend() override;
bool Initialize(bool upload_vram, Error* error) override;
bool UpdateSettings(const GPUSettings& old_settings, Error* error) override;
u32 GetResolutionScale() const override;
bool UpdateResolutionScale(Error* error) override;
void RestoreDeviceContext() override;
void FlushRender() override;
void ReadVRAM(u32 x, u32 y, u32 width, u32 height) override;
void FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color, bool interlaced_rendering,
u8 interlaced_display_field) override;
void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data, bool set_mask, bool check_mask) override;
void CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height, bool set_mask,
bool check_mask) override;
void DrawPolygon(const GPUBackendDrawPolygonCommand* cmd) override;
void DrawPrecisePolygon(const GPUBackendDrawPrecisePolygonCommand* cmd) override;
void DrawSprite(const GPUBackendDrawRectangleCommand* cmd) override;
void DrawLine(const GPUBackendDrawLineCommand* cmd) override;
void DrawPreciseLine(const GPUBackendDrawPreciseLineCommand* cmd) override;
void DrawingAreaChanged() override;
void ClearCache() override;
void OnBufferSwapped() override;
void ClearVRAM() override;
void UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd) override;
void LoadState(const GPUBackendLoadStateCommand* cmd) override;
bool AllocateMemorySaveState(System::MemorySaveState& mss, Error* error) override;
void DoMemoryState(StateWrapper& sw, System::MemorySaveState& mss) override;
};
} // namespace
GPUNullBackend::GPUNullBackend(GPUPresenter& presenter) : GPUBackend(presenter)
{
}
GPUNullBackend::~GPUNullBackend() = default;
bool GPUNullBackend::Initialize(bool upload_vram, Error* error)
{
return GPUBackend::Initialize(upload_vram, error);
}
bool GPUNullBackend::UpdateSettings(const GPUSettings& old_settings, Error* error)
{
return GPUBackend::UpdateSettings(old_settings, error);
}
u32 GPUNullBackend::GetResolutionScale() const
{
return 1;
}
bool GPUNullBackend::UpdateResolutionScale(Error* error)
{
return true;
}
void GPUNullBackend::RestoreDeviceContext()
{
}
void GPUNullBackend::FlushRender()
{
}
void GPUNullBackend::ReadVRAM(u32 x, u32 y, u32 width, u32 height)
{
}
void GPUNullBackend::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color, bool interlaced_rendering,
u8 interlaced_display_field)
{
}
void GPUNullBackend::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data, bool set_mask, bool check_mask)
{
}
void GPUNullBackend::CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height, bool set_mask,
bool check_mask)
{
}
void GPUNullBackend::DrawPolygon(const GPUBackendDrawPolygonCommand* cmd)
{
}
void GPUNullBackend::DrawPrecisePolygon(const GPUBackendDrawPrecisePolygonCommand* cmd)
{
}
void GPUNullBackend::DrawSprite(const GPUBackendDrawRectangleCommand* cmd)
{
}
void GPUNullBackend::DrawLine(const GPUBackendDrawLineCommand* cmd)
{
}
void GPUNullBackend::DrawPreciseLine(const GPUBackendDrawPreciseLineCommand* cmd)
{
}
void GPUNullBackend::DrawingAreaChanged()
{
}
void GPUNullBackend::ClearCache()
{
}
void GPUNullBackend::OnBufferSwapped()
{
}
void GPUNullBackend::ClearVRAM()
{
}
void GPUNullBackend::UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd)
{
}
void GPUNullBackend::LoadState(const GPUBackendLoadStateCommand* cmd)
{
}
bool GPUNullBackend::AllocateMemorySaveState(System::MemorySaveState& mss, Error* error)
{
return false;
}
void GPUNullBackend::DoMemoryState(StateWrapper& sw, System::MemorySaveState& mss)
{
}
std::unique_ptr<GPUBackend> GPUBackend::CreateNullBackend(GPUPresenter& presenter)
{
return std::make_unique<GPUNullBackend>(presenter);
}

View File

@ -56,6 +56,7 @@ public:
static std::unique_ptr<GPUBackend> CreateHardwareBackend(GPUPresenter& presenter); static std::unique_ptr<GPUBackend> CreateHardwareBackend(GPUPresenter& presenter);
static std::unique_ptr<GPUBackend> CreateSoftwareBackend(GPUPresenter& presenter); static std::unique_ptr<GPUBackend> CreateSoftwareBackend(GPUPresenter& presenter);
static std::unique_ptr<GPUBackend> CreateNullBackend(GPUPresenter& presenter);
static bool RenderScreenshotToBuffer(u32 width, u32 height, bool postfx, Image* out_image); static bool RenderScreenshotToBuffer(u32 width, u32 height, bool postfx, Image* out_image);
static void RenderScreenshotToFile(const std::string_view path, DisplayScreenshotMode mode, u8 quality, static void RenderScreenshotToFile(const std::string_view path, DisplayScreenshotMode mode, u8 quality,
@ -67,6 +68,8 @@ public:
static bool AllocateMemorySaveStates(std::span<System::MemorySaveState> states, Error* error); static bool AllocateMemorySaveStates(std::span<System::MemorySaveState> states, Error* error);
static void QueueUpdateResolutionScale();
public: public:
GPUBackend(GPUPresenter& presenter); GPUBackend(GPUPresenter& presenter);
virtual ~GPUBackend(); virtual ~GPUBackend();
@ -75,13 +78,13 @@ public:
virtual bool Initialize(bool upload_vram, Error* error); virtual bool Initialize(bool upload_vram, Error* error);
virtual void UpdateSettings(const GPUSettings& old_settings); virtual bool UpdateSettings(const GPUSettings& old_settings, Error* error);
/// Returns the current resolution scale. /// Returns the current resolution scale.
virtual u32 GetResolutionScale() const = 0; virtual u32 GetResolutionScale() const = 0;
/// Updates the resolution scale when it's set to automatic. /// Updates the resolution scale when it's set to automatic.
virtual void UpdateResolutionScale() = 0; virtual bool UpdateResolutionScale(Error* error) = 0;
// Graphics API state reset/restore - call when drawing the UI etc. // Graphics API state reset/restore - call when drawing the UI etc.
// TODO: replace with "invalidate cached state" // TODO: replace with "invalidate cached state"

View File

@ -277,11 +277,8 @@ bool GPU_HW::Initialize(bool upload_vram, Error* error)
if (m_use_texture_cache) if (m_use_texture_cache)
{ {
if (!GPUTextureCache::Initialize(this)) if (!GPUTextureCache::Initialize(this, error))
{ return false;
ERROR_LOG("Failed to initialize texture cache, disabling.");
m_use_texture_cache = false;
}
} }
else else
{ {
@ -435,9 +432,10 @@ void GPU_HW::RestoreDeviceContext()
m_batch_ubo_dirty = true; m_batch_ubo_dirty = true;
} }
void GPU_HW::UpdateSettings(const GPUSettings& old_settings) bool GPU_HW::UpdateSettings(const GPUSettings& old_settings, Error* error)
{ {
GPUBackend::UpdateSettings(old_settings); if (!GPUBackend::UpdateSettings(old_settings, error))
return false;
FlushRender(); FlushRender();
@ -543,21 +541,19 @@ void GPU_HW::UpdateSettings(const GPUSettings& old_settings)
if (shaders_changed) if (shaders_changed)
{ {
Error error; if (!CompilePipelines(error))
if (!CompilePipelines(&error))
{ {
ERROR_LOG("Failed to recompile pipelines: {}", error.GetDescription()); Error::AddPrefix(error, "Failed to recompile pipelines: ");
Panic("Failed to recompile pipelines."); return false;
} }
} }
else if (resolution_dependent_shaders_changed || downsampling_shaders_changed) else if (resolution_dependent_shaders_changed || downsampling_shaders_changed)
{ {
Error error; if ((resolution_dependent_shaders_changed && !CompileResolutionDependentPipelines(error)) ||
if ((resolution_dependent_shaders_changed && !CompileResolutionDependentPipelines(&error)) || (downsampling_shaders_changed && !CompileDownsamplePipelines(error)))
(downsampling_shaders_changed && !CompileDownsamplePipelines(&error)))
{ {
ERROR_LOG("Failed to recompile resolution dependent pipelines: {}", error.GetDescription()); Error::AddPrefix(error, "Failed to recompile resolution dependent pipelines: ");
Panic("Failed to recompile resolution dependent pipelines."); return false;
} }
} }
@ -568,11 +564,10 @@ void GPU_HW::UpdateSettings(const GPUSettings& old_settings)
g_gpu_device->PurgeTexturePool(); g_gpu_device->PurgeTexturePool();
g_gpu_device->WaitForGPUIdle(); g_gpu_device->WaitForGPUIdle();
Error error; if (!CreateBuffers(error))
if (!CreateBuffers(&error))
{ {
ERROR_LOG("Failed to recreate buffers: {}", error.GetDescription()); Error::AddPrefix(error, "Failed to recreate buffers: ");
Panic("Failed to recreate buffers."); return false;
} }
UpdateDownsamplingLevels(); UpdateDownsamplingLevels();
@ -591,10 +586,10 @@ void GPU_HW::UpdateSettings(const GPUSettings& old_settings)
if (m_use_texture_cache && !old_settings.gpu_texture_cache) if (m_use_texture_cache && !old_settings.gpu_texture_cache)
{ {
if (!GPUTextureCache::Initialize(this)) if (!GPUTextureCache::Initialize(this, error))
{ {
ERROR_LOG("Failed to initialize texture cache, disabling."); Error::AddPrefix(error, "Failed to initialize texture cache: ");
m_use_texture_cache = false; return false;
} }
} }
else if (!m_use_texture_cache && old_settings.gpu_texture_cache) else if (!m_use_texture_cache && old_settings.gpu_texture_cache)
@ -602,7 +597,8 @@ void GPU_HW::UpdateSettings(const GPUSettings& old_settings)
GPUTextureCache::Shutdown(); GPUTextureCache::Shutdown();
} }
GPUTextureCache::UpdateSettings(m_use_texture_cache, old_settings); if (!GPUTextureCache::UpdateSettings(m_use_texture_cache, old_settings, error))
return false;
if (g_gpu_settings.gpu_downsample_mode != old_settings.gpu_downsample_mode || if (g_gpu_settings.gpu_downsample_mode != old_settings.gpu_downsample_mode ||
(g_gpu_settings.gpu_downsample_mode == GPUDownsampleMode::Box && (g_gpu_settings.gpu_downsample_mode == GPUDownsampleMode::Box &&
@ -622,6 +618,8 @@ void GPU_HW::UpdateSettings(const GPUSettings& old_settings)
m_draw_mode.mode_reg.texture_mode == GPUTextureMode::Palette8Bit); m_draw_mode.mode_reg.texture_mode == GPUTextureMode::Palette8Bit);
} }
} }
return true;
} }
void GPU_HW::CheckSettings() void GPU_HW::CheckSettings()
@ -783,10 +781,12 @@ u32 GPU_HW::CalculateResolutionScale() const
return std::clamp<u32>(scale, 1, GetMaxResolutionScale()); return std::clamp<u32>(scale, 1, GetMaxResolutionScale());
} }
void GPU_HW::UpdateResolutionScale() bool GPU_HW::UpdateResolutionScale(Error* error)
{ {
if (CalculateResolutionScale() != m_resolution_scale) if (CalculateResolutionScale() == m_resolution_scale)
UpdateSettings(g_settings); return true;
return UpdateSettings(g_settings, error);
} }
GPUDownsampleMode GPU_HW::GetDownsampleMode(u32 resolution_scale) const GPUDownsampleMode GPU_HW::GetDownsampleMode(u32 resolution_scale) const
@ -900,8 +900,6 @@ GPUTexture::Format GPU_HW::GetDepthBufferFormat() const
bool GPU_HW::CreateBuffers(Error* error) bool GPU_HW::CreateBuffers(Error* error)
{ {
DestroyBuffers();
// scale vram size to internal resolution // scale vram size to internal resolution
const u32 texture_width = VRAM_WIDTH * m_resolution_scale; const u32 texture_width = VRAM_WIDTH * m_resolution_scale;
const u32 texture_height = VRAM_HEIGHT * m_resolution_scale; const u32 texture_height = VRAM_HEIGHT * m_resolution_scale;

View File

@ -67,10 +67,9 @@ public:
void RestoreDeviceContext() override; void RestoreDeviceContext() override;
void FlushRender() override; void FlushRender() override;
protected: bool UpdateSettings(const GPUSettings& old_settings, Error* error) override;
void UpdateSettings(const GPUSettings& old_settings) override;
void UpdateResolutionScale() override; bool UpdateResolutionScale(Error* error) override;
void FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color, bool interlaced_rendering, u8 active_line_lsb) override; void FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color, bool interlaced_rendering, u8 active_line_lsb) override;
void ReadVRAM(u32 x, u32 y, u32 width, u32 height) override; void ReadVRAM(u32 x, u32 y, u32 width, u32 height) override;

View File

@ -249,7 +249,7 @@ static bool IsDumpingVRAMWriteTextures();
static void UpdateVRAMTrackingState(); static void UpdateVRAMTrackingState();
static void SetHashCacheTextureFormat(); static void SetHashCacheTextureFormat();
static bool CompilePipelines(); static bool CompilePipelines(Error* error);
static void DestroyPipelines(); static void DestroyPipelines();
static const Source* ReturnSource(Source* source, const GSVector4i uv_rect, PaletteRecordFlags flags); static const Source* ReturnSource(Source* source, const GSVector4i uv_rect, PaletteRecordFlags flags);
@ -581,20 +581,20 @@ bool GPUTextureCache::IsDumpingVRAMWriteTextures()
return (g_gpu_settings.texture_replacements.dump_textures && !s_state.config.dump_texture_pages); return (g_gpu_settings.texture_replacements.dump_textures && !s_state.config.dump_texture_pages);
} }
bool GPUTextureCache::Initialize(GPU_HW* backend) bool GPUTextureCache::Initialize(GPU_HW* backend, Error* error)
{ {
s_state.hw_backend = backend; s_state.hw_backend = backend;
SetHashCacheTextureFormat(); SetHashCacheTextureFormat();
ReloadTextureReplacements(false); ReloadTextureReplacements(false);
UpdateVRAMTrackingState(); UpdateVRAMTrackingState();
if (!CompilePipelines()) if (!CompilePipelines(error))
return false; return false;
return true; return true;
} }
void GPUTextureCache::UpdateSettings(bool use_texture_cache, const GPUSettings& old_settings) bool GPUTextureCache::UpdateSettings(bool use_texture_cache, const GPUSettings& old_settings, Error* error)
{ {
const bool prev_tracking_state = s_state.track_vram_writes; const bool prev_tracking_state = s_state.track_vram_writes;
@ -613,8 +613,11 @@ void GPUTextureCache::UpdateSettings(bool use_texture_cache, const GPUSettings&
s_state.config.replacement_scale_linear_filter != old_replacement_scale_linear_filter) s_state.config.replacement_scale_linear_filter != old_replacement_scale_linear_filter)
{ {
DestroyPipelines(); DestroyPipelines();
if (!CompilePipelines()) [[unlikely]] if (!CompilePipelines(error)) [[unlikely]]
Panic("Failed to compile pipelines on TC replacement settings change"); {
Error::AddPrefix(error, "Failed to compile pipelines on TC replacement settings change: ");
return false;
}
} }
} }
@ -625,6 +628,8 @@ void GPUTextureCache::UpdateSettings(bool use_texture_cache, const GPUSettings&
if (s_state.track_vram_writes != prev_tracking_state) if (s_state.track_vram_writes != prev_tracking_state)
Invalidate(); Invalidate();
return true;
} }
bool GPUTextureCache::GetStateSize(StateWrapper& sw, u32* size) bool GPUTextureCache::GetStateSize(StateWrapper& sw, u32* size)
@ -828,7 +833,7 @@ void GPUTextureCache::SetHashCacheTextureFormat()
INFO_LOG("Using {} format for hash cache entries.", GPUTexture::GetFormatName(s_state.hash_cache_texture_format)); INFO_LOG("Using {} format for hash cache entries.", GPUTexture::GetFormatName(s_state.hash_cache_texture_format));
} }
bool GPUTextureCache::CompilePipelines() bool GPUTextureCache::CompilePipelines(Error* error)
{ {
if (!g_gpu_settings.texture_replacements.enable_texture_replacements) if (!g_gpu_settings.texture_replacements.enable_texture_replacements)
return true; return true;

View File

@ -5,6 +5,7 @@
#include "gpu_types.h" #include "gpu_types.h"
class Error;
class Image; class Image;
class GPUTexture; class GPUTexture;
class StateWrapper; class StateWrapper;
@ -103,8 +104,8 @@ struct Source
TListNode<Source> hash_cache_ref; TListNode<Source> hash_cache_ref;
}; };
bool Initialize(GPU_HW* backend); bool Initialize(GPU_HW* backend, Error* error);
void UpdateSettings(bool use_texture_cache, const GPUSettings& old_settings); bool UpdateSettings(bool use_texture_cache, const GPUSettings& old_settings, Error* error);
bool GetStateSize(StateWrapper& sw, u32* size); bool GetStateSize(StateWrapper& sw, u32* size);
bool DoState(StateWrapper& sw, bool skip); bool DoState(StateWrapper& sw, bool skip);

View File

@ -56,7 +56,7 @@ bool GPUPresenter::Initialize(Error* error)
return true; return true;
} }
void GPUPresenter::UpdateSettings(const GPUSettings& old_settings) bool GPUPresenter::UpdateSettings(const GPUSettings& old_settings, Error* error)
{ {
if (g_gpu_settings.display_scaling != old_settings.display_scaling || if (g_gpu_settings.display_scaling != old_settings.display_scaling ||
g_gpu_settings.display_deinterlacing_mode != old_settings.display_deinterlacing_mode || g_gpu_settings.display_deinterlacing_mode != old_settings.display_deinterlacing_mode ||
@ -69,11 +69,14 @@ void GPUPresenter::UpdateSettings(const GPUSettings& old_settings)
if (!CompileDisplayPipelines( if (!CompileDisplayPipelines(
g_gpu_settings.display_scaling != old_settings.display_scaling, g_gpu_settings.display_scaling != old_settings.display_scaling,
g_gpu_settings.display_deinterlacing_mode != old_settings.display_deinterlacing_mode, g_gpu_settings.display_deinterlacing_mode != old_settings.display_deinterlacing_mode,
g_gpu_settings.display_24bit_chroma_smoothing != old_settings.display_24bit_chroma_smoothing, nullptr)) g_gpu_settings.display_24bit_chroma_smoothing != old_settings.display_24bit_chroma_smoothing, error))
{ {
Panic("Failed to compile display pipeline on settings change."); Error::AddPrefix(error, "Failed to compile display pipeline on settings change:\n");
return false;
} }
} }
return true;
} }
bool GPUPresenter::CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_smoothing, Error* error) bool GPUPresenter::CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_smoothing, Error* error)
@ -818,6 +821,7 @@ bool GPUPresenter::PresentFrame(GPUPresenter* presenter, GPUBackend* backend, bo
if (pres == GPUDevice::PresentResult::DeviceLost) [[unlikely]] if (pres == GPUDevice::PresentResult::DeviceLost) [[unlikely]]
{ {
ERROR_LOG("GPU device lost during present."); ERROR_LOG("GPU device lost during present.");
GPUThread::ReportFatalErrorAndShutdown("GPU device lost. The log may contain more information.");
return false; return false;
} }

View File

@ -42,7 +42,7 @@ public:
bool Initialize(Error* error); bool Initialize(Error* error);
void UpdateSettings(const GPUSettings& old_settings); bool UpdateSettings(const GPUSettings& old_settings, Error* error);
void ClearDisplay(); void ClearDisplay();
void ClearDisplayTexture(); void ClearDisplayTexture();

View File

@ -65,8 +65,9 @@ void GPU_SW::ClearVRAM()
std::memset(g_gpu_clut, 0, sizeof(g_gpu_clut)); std::memset(g_gpu_clut, 0, sizeof(g_gpu_clut));
} }
void GPU_SW::UpdateResolutionScale() bool GPU_SW::UpdateResolutionScale(Error* error)
{ {
return true;
} }
void GPU_SW::LoadState(const GPUBackendLoadStateCommand* cmd) void GPU_SW::LoadState(const GPUBackendLoadStateCommand* cmd)

View File

@ -27,7 +27,6 @@ public:
u32 GetResolutionScale() const override; u32 GetResolutionScale() const override;
protected:
void ReadVRAM(u32 x, u32 y, u32 width, u32 height) override; void ReadVRAM(u32 x, u32 y, u32 width, u32 height) override;
void FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color, bool interlaced_rendering, u8 active_line_lsb) override; void FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color, bool interlaced_rendering, u8 active_line_lsb) override;
void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data, bool set_mask, bool check_mask) override; void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data, bool set_mask, bool check_mask) override;
@ -47,7 +46,7 @@ protected:
void ClearVRAM() override; void ClearVRAM() override;
void UpdateResolutionScale() override; bool UpdateResolutionScale(Error* error) override;
void LoadState(const GPUBackendLoadStateCommand* cmd) override; void LoadState(const GPUBackendLoadStateCommand* cmd) override;

View File

@ -186,7 +186,7 @@ void GPUThread::Internal::SetThreadEnabled(bool enabled)
requested_fullscreen_ui, true, &error)) requested_fullscreen_ui, true, &error))
{ {
ERROR_LOG("Reconfigure failed: {}", error.GetDescription()); ERROR_LOG("Reconfigure failed: {}", error.GetDescription());
Panic("Failed to reconfigure when changing thread state."); ReportFatalErrorAndShutdown(fmt::format("Reconfigure failed: {}", error.GetDescription()));
} }
} }
@ -780,7 +780,8 @@ bool GPUThread::CreateGPUBackendOnThread(GPURenderer renderer, bool upload_vram,
s_state.requested_renderer = GPURenderer::Software; s_state.requested_renderer = GPURenderer::Software;
s_state.gpu_backend = GPUBackend::CreateSoftwareBackend(*s_state.gpu_presenter); s_state.gpu_backend = GPUBackend::CreateSoftwareBackend(*s_state.gpu_presenter);
okay = s_state.gpu_backend->Initialize(upload_vram, &local_error); if (!s_state.gpu_backend->Initialize(upload_vram, &local_error))
Panic("Failed to initialize fallback software renderer");
} }
if (!okay) if (!okay)
@ -971,8 +972,14 @@ void GPUThread::UpdateSettingsOnThread(const GPUSettings& old_settings)
PostProcessing::UpdateSettings(); PostProcessing::UpdateSettings();
s_state.gpu_presenter->UpdateSettings(old_settings); Error error;
s_state.gpu_backend->UpdateSettings(old_settings); if (!s_state.gpu_presenter->UpdateSettings(old_settings, &error) ||
!s_state.gpu_backend->UpdateSettings(old_settings, &error)) [[unlikely]]
{
ReportFatalErrorAndShutdown(fmt::format("Failed to update settings: {}", error.GetDescription()));
return;
}
if (ImGuiManager::UpdateDebugWindowConfig() || (PostProcessing::DisplayChain.IsActive() && !IsSystemPaused())) if (ImGuiManager::UpdateDebugWindowConfig() || (PostProcessing::DisplayChain.IsActive() && !IsSystemPaused()))
PresentFrameAndRestoreContext(); PresentFrameAndRestoreContext();
else else
@ -1071,6 +1078,21 @@ void GPUThread::UpdateSettings(bool gpu_settings_changed, bool device_settings_c
} }
} }
void GPUThread::ReportFatalErrorAndShutdown(std::string_view reason)
{
DebugAssert(IsOnThread());
std::string message = fmt::format("GPU thread shut down with fatal error:\n\n{}", reason);
Host::RunOnCPUThread([message = std::move(message)]() { System::AbnormalShutdown(message); });
// replace the renderer with a dummy/null backend, so that all commands get dropped
ERROR_LOG("Switching to null renderer: {}", reason);
s_state.gpu_backend.reset();
s_state.gpu_backend = GPUBackend::CreateNullBackend(*s_state.gpu_presenter);
if (!s_state.gpu_backend->Initialize(false, nullptr)) [[unlikely]]
Panic("Failed to initialize null GPU backend");
}
bool GPUThread::IsOnThread() bool GPUThread::IsOnThread()
{ {
return (!s_state.use_gpu_thread || s_state.gpu_thread.IsCallingThread()); return (!s_state.use_gpu_thread || s_state.gpu_thread.IsCallingThread());
@ -1193,7 +1215,11 @@ void GPUThread::DisplayWindowResizedOnThread()
} }
if (g_gpu_settings.gpu_resolution_scale == 0) if (g_gpu_settings.gpu_resolution_scale == 0)
s_state.gpu_backend->UpdateResolutionScale(); {
Error error;
if (!s_state.gpu_backend->UpdateResolutionScale(&error)) [[unlikely]]
ReportFatalErrorAndShutdown(fmt::format("Failed to update resolution scale: {}", error.GetDescription()));
}
} }
} }

View File

@ -68,6 +68,9 @@ const WindowInfo& GetRenderWindowInfo();
void UpdateSettings(bool gpu_settings_changed, bool device_settings_changed); void UpdateSettings(bool gpu_settings_changed, bool device_settings_changed);
/// Triggers an abnormal system shutdown and waits for it to destroy the backend.
void ReportFatalErrorAndShutdown(std::string_view reason);
bool IsOnThread(); bool IsOnThread();
bool IsUsingThread(); bool IsUsingThread();
void RunOnThread(AsyncCallType func); void RunOnThread(AsyncCallType func);

View File

@ -2005,6 +2005,24 @@ void System::DestroySystem()
Host::OnSystemDestroyed(); Host::OnSystemDestroyed();
} }
void System::AbnormalShutdown(const std::string_view reason)
{
if (!IsValid())
return;
ERROR_LOG("Abnormal shutdown: {}", reason);
Host::OnSystemAbnormalShutdown(reason);
// Immediately switch to destroying and exit execution to get out of here.
s_state.state = State::Stopping;
std::atomic_thread_fence(std::memory_order_release);
if (s_state.system_executing)
InterruptExecution();
else
DestroySystem();
}
void System::ClearRunningGame() void System::ClearRunningGame()
{ {
UpdateSessionTime(s_state.running_game_serial); UpdateSessionTime(s_state.running_game_serial);
@ -4636,8 +4654,11 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
Error error; Error error;
if (!Bus::ReallocateMemoryMap(g_settings.export_shared_memory, &error)) [[unlikely]] if (!Bus::ReallocateMemoryMap(g_settings.export_shared_memory, &error)) [[unlikely]]
{ {
ERROR_LOG(error.GetDescription()); if (IsValid())
Panic("Failed to reallocate memory map. The log may contain more information."); {
AbnormalShutdown(fmt::format("Failed to reallocate memory map: {}", error.GetDescription()));
return;
}
} }
} }

View File

@ -50,6 +50,9 @@ void DisplayWindowResized();
/// Updates the internal GTE aspect ratio. Use with "match display" aspect ratio setting. /// Updates the internal GTE aspect ratio. Use with "match display" aspect ratio setting.
void UpdateGTEAspectRatio(); void UpdateGTEAspectRatio();
/// Immediately terminates the virtual machine, no state is saved.
void AbnormalShutdown(const std::string_view reason);
/// Performs mandatory hardware checks. /// Performs mandatory hardware checks.
bool PerformEarlyHardwareChecks(Error* error); bool PerformEarlyHardwareChecks(Error* error);
@ -96,6 +99,9 @@ void OnSystemPaused();
/// Called when the VM is resumed after being paused. /// Called when the VM is resumed after being paused.
void OnSystemResumed(); void OnSystemResumed();
/// Called when the VM abnormally exits because an error has occurred, and it cannot continue.
void OnSystemAbnormalShutdown(const std::string_view reason);
/// Called when performance metrics are updated, approximately once a second. /// Called when performance metrics are updated, approximately once a second.
void OnPerformanceCountersUpdated(const GPUBackend* gpu_backend); void OnPerformanceCountersUpdated(const GPUBackend* gpu_backend);

View File

@ -1045,6 +1045,18 @@ void Host::OnSystemDestroyed()
emit g_emu_thread->systemDestroyed(); emit g_emu_thread->systemDestroyed();
} }
void Host::OnSystemAbnormalShutdown(const std::string_view reason)
{
Host::ReportErrorAsync(
TRANSLATE_SV("QtHost", "Error"),
fmt::format(
TRANSLATE_FS("QtHost",
"Unfortunately, the virtual machine has abnormally shut down and cannot be recovered. Please use "
"the available support options for further assistance, and provide information about what you were "
"doing when the error occurred, as well as the details below:\n\n{}"),
reason));
}
void Host::OnGPUThreadRunIdleChanged(bool is_active) void Host::OnGPUThreadRunIdleChanged(bool is_active)
{ {
g_emu_thread->setGPUThreadRunIdle(is_active); g_emu_thread->setGPUThreadRunIdle(is_active);

View File

@ -293,6 +293,11 @@ void Host::OnSystemResumed()
// //
} }
void Host::OnSystemAbnormalShutdown(const std::string_view reason)
{
// Already logged in core.
}
void Host::OnGPUThreadRunIdleChanged(bool is_active) void Host::OnGPUThreadRunIdleChanged(bool is_active)
{ {
// //