GPUThread: Store copy of game info

Don't save it in FullscreenUI.
This commit is contained in:
Stenzek 2025-07-20 12:44:57 +10:00
parent 5fd61b6e3a
commit 4440ae3b78
No known key found for this signature in database
6 changed files with 178 additions and 122 deletions

View File

@ -432,7 +432,7 @@ static GPUTexture* GetTextureForGameListEntryType(GameList::EntryType type);
static GPUTexture* GetGameListCover(const GameList::Entry* entry, bool fallback_to_achievements_icon,
bool fallback_to_icon);
static GPUTexture* GetGameListCoverTrophy(const GameList::Entry* entry, const ImVec2& image_size);
static GPUTexture* GetCoverForCurrentGame();
static GPUTexture* GetCoverForCurrentGame(const std::string& game_path);
static void SwitchToAchievements();
static void SwitchToLeaderboards();
@ -514,11 +514,7 @@ struct ALIGN_TO_CACHE_LINE UIState
bool tried_to_initialize = false;
bool pause_menu_was_open = false;
bool was_paused_on_quick_menu_open = false;
std::string current_game_title;
std::string current_game_serial;
std::string current_game_path;
std::string achievements_user_badge_path;
GameHash current_game_hash = 0;
// Resources
std::shared_ptr<GPUTexture> app_icon_texture;
@ -722,23 +718,11 @@ bool FullscreenUI::Initialize()
s_state.initialized = true;
// in case we open the pause menu while the game is running
const bool open_main_window = (s_state.current_main_window == MainWindowType::None && !GPUThread::HasGPUBackend() &&
!GPUThread::IsGPUBackendRequested());
if (GPUThread::HasGPUBackend())
{
Host::RunOnCPUThread([]() {
if (System::IsValid())
{
FullscreenUI::OnRunningGameChanged(System::GetDiscPath(), System::GetGameSerial(), System::GetGameTitle(),
System::GetGameHash());
}
});
}
LoadBackground();
if (open_main_window)
// in case we open the pause menu while the game is running
if (s_state.current_main_window == MainWindowType::None && !GPUThread::HasGPUBackend() &&
!GPUThread::IsGPUBackendRequested())
{
ReturnToMainWindow();
ForceKeyNavEnabled();
@ -1018,24 +1002,6 @@ void FullscreenUI::OnSystemDestroyed()
});
}
void FullscreenUI::OnRunningGameChanged(const std::string& path, const std::string& serial, const std::string& title,
GameHash hash)
{
// NOTE: Called on CPU thread.
if (!IsInitialized())
return;
GPUThread::RunOnThread([path = path, title = title, serial = serial, hash = hash]() mutable {
if (!IsInitialized())
return;
s_state.current_game_title = std::move(title);
s_state.current_game_serial = std::move(serial);
s_state.current_game_path = std::move(path);
s_state.current_game_hash = hash;
});
}
void FullscreenUI::PauseForMenuOpen(bool set_pause_menu_open)
{
s_state.was_paused_on_quick_menu_open = GPUThread::IsSystemPaused();
@ -1233,10 +1199,6 @@ void FullscreenUI::Shutdown(bool clear_state)
s_state.fullscreen_mode_list_cache = {};
s_state.graphics_adapter_list_cache = {};
s_state.hotkey_list_cache = {};
s_state.current_game_hash = 0;
s_state.current_game_path = {};
s_state.current_game_serial = {};
s_state.current_game_title = {};
}
DestroyResources();
@ -1657,7 +1619,7 @@ void FullscreenUI::StartChangeDiscFromFile()
};
OpenFileSelector(FSUI_ICONVSTR(ICON_FA_COMPACT_DISC, "Select Disc Image"), false, std::move(callback),
GetDiscImageFilters(), std::string(Path::GetDirectory(s_state.current_game_path)));
GetDiscImageFilters(), std::string(Path::GetDirectory(GPUThread::GetGamePath())));
}
void FullscreenUI::BeginChangeDiscOnCPUThread(bool needs_pause)
@ -3735,14 +3697,15 @@ void FullscreenUI::SwitchToGameSettingsForSerial(std::string_view serial, GameHa
bool FullscreenUI::SwitchToGameSettings()
{
if (s_state.current_game_serial.empty())
const std::string& serial = GPUThread::GetGameSerial();
if (serial.empty())
return false;
auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryForPath(s_state.current_game_path);
const GameList::Entry* entry = GameList::GetEntryForPath(GPUThread::GetGamePath());
if (!entry)
{
SwitchToGameSettingsForSerial(s_state.current_game_serial, s_state.current_game_hash);
SwitchToGameSettingsForSerial(serial, GPUThread::GetGameHash());
return true;
}
else
@ -6999,7 +6962,10 @@ void FullscreenUI::DrawPauseMenu()
dl->AddRectFilled(ImVec2(0.0f, 0.0f), ImVec2(display_size.x, scaled_top_bar_height),
ImGui::GetColorU32(ModAlpha(UIStyle.BackgroundColor, 0.95f)), 0.0f);
GPUTexture* const cover = GetCoverForCurrentGame();
const std::string& game_title = GPUThread::GetGameTitle();
const std::string& game_serial = GPUThread::GetGameSerial();
const std::string& game_path = GPUThread::GetGamePath();
GPUTexture* const cover = GetCoverForCurrentGame(game_path);
const float image_padding = LayoutScale(5.0f); // compensate for font baseline
const float image_size = scaled_top_bar_height - scaled_top_bar_padding - scaled_top_bar_padding - image_padding;
const ImRect image_rect(
@ -7008,14 +6974,14 @@ void FullscreenUI::DrawPauseMenu()
ImVec2(static_cast<float>(cover->GetWidth()), static_cast<float>(cover->GetHeight()))));
dl->AddImage(cover, image_rect.Min, image_rect.Max);
if (!s_state.current_game_serial.empty())
buffer.format("{} - {}", s_state.current_game_serial, Path::GetFileName(s_state.current_game_path));
if (!game_serial.empty())
buffer.format("{} - {}", game_serial, Path::GetFileName(game_path));
else
buffer.assign(Path::GetFileName(s_state.current_game_path));
buffer.assign(Path::GetFileName(game_path));
ImVec2 text_pos = ImVec2(scaled_top_bar_padding + image_size + scaled_top_bar_padding, scaled_top_bar_padding);
RenderShadowedTextClipped(dl, UIStyle.Font, UIStyle.LargeFontSize, UIStyle.BoldFontWeight, text_pos, display_size,
title_text_color, s_state.current_game_title);
title_text_color, game_title);
text_pos.y += UIStyle.LargeFontSize + scaled_text_spacing;
if (Achievements::IsActive())
@ -7042,9 +7008,9 @@ void FullscreenUI::DrawPauseMenu()
title_text_color, buffer);
text_pos.y += UIStyle.LargeFontSize + scaled_text_spacing;
if (!s_state.current_game_serial.empty())
if (!game_serial.empty())
{
const std::time_t cached_played_time = GameList::GetCachedPlayedTimeForSerial(s_state.current_game_serial);
const std::time_t cached_played_time = GameList::GetCachedPlayedTimeForSerial(game_serial);
const std::time_t session_time = static_cast<std::time_t>(System::GetSessionPlayedTime());
buffer.format(FSUI_FSTR("Session: {}"), GameList::FormatTimespan(session_time, true));
@ -7110,14 +7076,12 @@ void FullscreenUI::DrawPauseMenu()
if (MenuButtonWithoutSummary(FSUI_ICONVSTR(ICON_PF_DOWNLOAD, "Load State"), has_game))
{
BeginTransition(
[]() { OpenSaveStateSelector(s_state.current_game_serial, s_state.current_game_path, true); });
BeginTransition([]() { OpenSaveStateSelector(GPUThread::GetGameSerial(), GPUThread::GetGamePath(), true); });
}
if (MenuButtonWithoutSummary(FSUI_ICONVSTR(ICON_PF_DISKETTE, "Save State"), has_game))
{
BeginTransition(
[]() { OpenSaveStateSelector(s_state.current_game_serial, s_state.current_game_path, false); });
BeginTransition([]() { OpenSaveStateSelector(GPUThread::GetGameSerial(), GPUThread::GetGamePath(), false); });
}
if (MenuButtonWithoutSummary(FSUI_ICONVSTR(ICON_PF_GAMEPAD_ALT, "Toggle Analog")))
@ -8925,11 +8889,11 @@ GPUTexture* FullscreenUI::GetTextureForGameListEntryType(GameList::EntryType typ
}
}
GPUTexture* FullscreenUI::GetCoverForCurrentGame()
GPUTexture* FullscreenUI::GetCoverForCurrentGame(const std::string& game_path)
{
auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryForPath(s_state.current_game_path);
const GameList::Entry* entry = GameList::GetEntryForPath(game_path);
if (!entry)
return s_state.fallback_disc_texture.get();

View File

@ -30,7 +30,6 @@ void OnSystemStarting();
void OnSystemPaused();
void OnSystemResumed();
void OnSystemDestroyed();
void OnRunningGameChanged(const std::string& path, const std::string& serial, const std::string& title, GameHash hash);
void Shutdown(bool clear_state);
void Render();

View File

@ -55,9 +55,8 @@ static constexpr u32 THREAD_SPIN_TIME_US = 50;
static constexpr u32 THREAD_SPIN_TIME_US = 200;
#endif
static bool Reconfigure(std::string serial, std::optional<GPURenderer> renderer, bool upload_vram,
std::optional<bool> fullscreen, std::optional<bool> start_fullscreen_ui, bool recreate_device,
Error* error);
static bool Reconfigure(std::optional<GPURenderer> renderer, bool upload_vram, std::optional<bool> fullscreen,
std::optional<bool> start_fullscreen_ui, bool recreate_device, Error* error);
// NOTE: Use with care! The handler needs to manually run the destructor.
template<class T, typename... Args>
@ -85,6 +84,9 @@ static void DestroyGPUPresenterOnThread();
static void SetThreadEnabled(bool enabled);
static void UpdateSettingsOnThread(GPUSettings&& new_settings);
static void UpdateGameInfoOnThread(GPUThreadUpdateGameInfoCommand* cmd);
static void GameInfoChanged(bool serial_changed);
static void ClearGameInfoOnThread();
static void UpdateRunIdle();
@ -115,7 +117,10 @@ struct ALIGN_TO_CACHE_LINE State
GPUVSyncMode requested_vsync = GPUVSyncMode::Disabled;
bool requested_allow_present_throttle = false;
bool requested_fullscreen_ui = false;
std::string game_title;
std::string game_serial;
std::string game_path;
GameHash game_hash = 0;
};
} // namespace
@ -463,6 +468,14 @@ void GPUThread::Internal::GPUThreadEntryPoint()
}
break;
case GPUBackendCommandType::UpdateGameInfo:
{
GPUThreadUpdateGameInfoCommand* ccmd = static_cast<GPUThreadUpdateGameInfoCommand*>(cmd);
UpdateGameInfoOnThread(ccmd);
ccmd->~GPUThreadUpdateGameInfoCommand();
}
break;
case GPUBackendCommandType::Shutdown:
{
// Should have consumed everything, and be shutdown.
@ -498,15 +511,13 @@ void GPUThread::Internal::DoRunIdle()
g_gpu_device->GetMainSwapChain()->ThrottlePresentation();
}
bool GPUThread::Reconfigure(std::string serial, std::optional<GPURenderer> renderer, bool upload_vram,
std::optional<bool> fullscreen, std::optional<bool> start_fullscreen_ui,
bool recreate_device, Error* error)
bool GPUThread::Reconfigure(std::optional<GPURenderer> renderer, bool upload_vram, std::optional<bool> fullscreen,
std::optional<bool> start_fullscreen_ui, bool recreate_device, Error* error)
{
INFO_LOG("Reconfiguring GPU thread.");
bool result = false;
GPUThreadReconfigureCommand* cmd = AllocateCommand<GPUThreadReconfigureCommand>(GPUBackendCommandType::Reconfigure);
cmd->game_serial = std::move(serial);
cmd->renderer = renderer;
cmd->fullscreen = fullscreen;
cmd->start_fullscreen_ui = start_fullscreen_ui;
@ -535,7 +546,7 @@ bool GPUThread::StartFullscreenUI(bool fullscreen, Error* error)
return true;
}
return Reconfigure(std::string(), std::nullopt, false, fullscreen, true, false, error);
return Reconfigure(std::nullopt, false, fullscreen, true, false, error);
}
bool GPUThread::IsFullscreenUIRequested()
@ -552,7 +563,7 @@ void GPUThread::StopFullscreenUI()
return;
}
Reconfigure(std::string(), std::nullopt, false, std::nullopt, false, false, nullptr);
Reconfigure(std::nullopt, false, std::nullopt, false, false, nullptr);
}
std::optional<GPURenderer> GPUThread::GetRequestedRenderer()
@ -560,17 +571,17 @@ std::optional<GPURenderer> GPUThread::GetRequestedRenderer()
return s_state.requested_renderer;
}
bool GPUThread::CreateGPUBackend(std::string serial, GPURenderer renderer, bool upload_vram, bool fullscreen,
bool force_recreate_device, Error* error)
bool GPUThread::CreateGPUBackend(GPURenderer renderer, bool upload_vram, bool fullscreen, bool force_recreate_device,
Error* error)
{
s_state.requested_renderer = renderer;
return Reconfigure(std::move(serial), renderer, upload_vram, fullscreen ? std::optional<bool>(true) : std::nullopt,
std::nullopt, force_recreate_device, error);
return Reconfigure(renderer, upload_vram, fullscreen ? std::optional<bool>(true) : std::nullopt, std::nullopt,
force_recreate_device, error);
}
void GPUThread::DestroyGPUBackend()
{
Reconfigure(std::string(), std::nullopt, false, std::nullopt, std::nullopt, false, nullptr);
Reconfigure(std::nullopt, false, std::nullopt, std::nullopt, false, nullptr);
s_state.requested_renderer.reset();
}
@ -779,15 +790,14 @@ void GPUThread::ReconfigureOnThread(GPUThreadReconfigureCommand* cmd)
// Are we shutting down everything?
if (!cmd->renderer.has_value() && !s_state.requested_fullscreen_ui)
{
// Serial clear must be after backend destroy, otherwise textures won't dump.
DestroyGPUBackendOnThread();
DestroyGPUPresenterOnThread();
DestroyDeviceOnThread(true);
s_state.game_serial = {};
ClearGameInfoOnThread();
return;
}
// Serial clear must be after backend destroy, otherwise textures won't dump.
s_state.game_serial = std::move(cmd->game_serial);
g_gpu_settings = std::move(cmd->settings);
// Readback old VRAM for hardware renderers.
@ -844,6 +854,8 @@ void GPUThread::ReconfigureOnThread(GPUThreadReconfigureCommand* cmd)
INFO_LOG("GPU device recreated in {:.2f}ms", timer.GetTimeMilliseconds());
}
// Full shutdown case handled above.
Assert(cmd->renderer.has_value() || s_state.requested_fullscreen_ui);
if (cmd->renderer.has_value())
{
Timer timer;
@ -862,6 +874,7 @@ void GPUThread::ReconfigureOnThread(GPUThreadReconfigureCommand* cmd)
// No point keeping the presenter around.
DestroyGPUBackendOnThread();
DestroyGPUPresenterOnThread();
ClearGameInfoOnThread();
}
}
@ -878,6 +891,7 @@ void GPUThread::ReconfigureOnThread(GPUThreadReconfigureCommand* cmd)
// Don't need to present game frames anymore.
DestroyGPUPresenterOnThread();
ClearGameInfoOnThread();
// Don't need timing to run FSUI.
g_gpu_device->SetGPUTimingEnabled(false);
@ -889,12 +903,6 @@ void GPUThread::ReconfigureOnThread(GPUThreadReconfigureCommand* cmd)
DestroyDeviceOnThread(true);
}
}
else
{
// Device is no longer needed.
DestroyGPUBackendOnThread();
DestroyDeviceOnThread(true);
}
}
void GPUThread::DestroyGPUBackendOnThread()
@ -965,7 +973,6 @@ void GPUThread::SetThreadEnabled(bool enabled)
const bool fullscreen = Host::IsFullscreen();
const bool requested_fullscreen_ui = s_state.requested_fullscreen_ui;
const std::optional<GPURenderer> requested_renderer = s_state.requested_renderer;
std::string serial = s_state.game_serial;
// Force VRAM download, we're recreating.
if (requested_renderer.has_value())
@ -979,7 +986,7 @@ void GPUThread::SetThreadEnabled(bool enabled)
}
// Shutdown reconfigure.
Reconfigure(std::string(), std::nullopt, false, false, false, false, nullptr);
Reconfigure(std::nullopt, false, false, false, false, nullptr);
// Thread should be idle at this point. Reset the FIFO.
ResetCommandFIFO();
@ -988,8 +995,8 @@ void GPUThread::SetThreadEnabled(bool enabled)
s_state.use_gpu_thread = enabled;
Error error;
if (!Reconfigure(std::move(serial), requested_renderer, requested_renderer.has_value(), fullscreen,
requested_fullscreen_ui, true, &error))
if (!Reconfigure(requested_renderer, requested_renderer.has_value(), fullscreen, requested_fullscreen_ui, true,
&error))
{
ERROR_LOG("Reconfigure failed: {}", error.GetDescription());
ReportFatalErrorAndShutdown(fmt::format("Reconfigure failed: {}", error.GetDescription()));
@ -1100,8 +1107,8 @@ void GPUThread::UpdateSettings(bool gpu_settings_changed, bool device_settings_c
INFO_LOG("Reconfiguring after device settings changed.");
Error error;
if (!Reconfigure(System::GetGameSerial(), s_state.requested_renderer, s_state.requested_renderer.has_value(),
std::nullopt, std::nullopt, true, &error)) [[unlikely]]
if (!Reconfigure(s_state.requested_renderer, s_state.requested_renderer.has_value(), std::nullopt, std::nullopt,
true, &error)) [[unlikely]]
{
Host::ReportErrorAsync("Error", fmt::format("Failed to recreate GPU device: {}", error.GetDescription()));
}
@ -1134,6 +1141,71 @@ void GPUThread::UpdateSettings(bool gpu_settings_changed, bool device_settings_c
}
}
void GPUThread::UpdateGameInfo(const std::string& title, const std::string& serial, const std::string& path,
GameHash hash, bool wake_thread /*= true*/)
{
if (!s_state.use_gpu_thread)
{
const bool serial_changed = (s_state.game_serial != serial);
s_state.game_title = title;
s_state.game_serial = serial;
s_state.game_path = path;
s_state.game_hash = hash;
GameInfoChanged(serial_changed);
return;
}
GPUThreadUpdateGameInfoCommand* cmd =
AllocateCommand<GPUThreadUpdateGameInfoCommand>(GPUBackendCommandType::UpdateGameInfo, title, serial, path, hash);
if (wake_thread)
PushCommandAndWakeThread(cmd);
else
PushCommand(cmd);
}
void GPUThread::ClearGameInfo()
{
if (!s_state.use_gpu_thread)
{
ClearGameInfoOnThread();
return;
}
PushCommandAndWakeThread(AllocateCommand<GPUThreadUpdateGameInfoCommand>(GPUBackendCommandType::UpdateGameInfo));
}
void GPUThread::UpdateGameInfoOnThread(GPUThreadUpdateGameInfoCommand* cmd)
{
DEV_LOG("Updating game info on GPU thread: {}/{}", cmd->game_serial, cmd->game_title);
const bool serial_changed = (s_state.game_serial != cmd->game_serial);
s_state.game_title = std::move(cmd->game_title);
s_state.game_serial = std::move(cmd->game_serial);
s_state.game_path = std::move(cmd->game_path);
s_state.game_hash = cmd->game_hash;
GameInfoChanged(serial_changed);
}
void GPUThread::GameInfoChanged(bool serial_changed)
{
if (!serial_changed)
return;
if (HasGPUBackend())
GPUTextureCache::GameSerialChanged();
if (SaveStateSelectorUI::IsOpen())
SaveStateSelectorUI::RefreshList();
}
void GPUThread::ClearGameInfoOnThread()
{
DEV_LOG("Clearing game info on GPU thread.");
s_state.game_hash = 0;
s_state.game_path = {};
s_state.game_serial = {};
s_state.game_title = {};
}
void GPUThread::ReportFatalErrorAndShutdown(std::string_view reason)
{
DebugAssert(IsOnThread());
@ -1389,24 +1461,26 @@ void GPUThread::UpdateRunIdle()
Host::OnGPUThreadRunIdleChanged(new_flag);
}
const std::string& GPUThread::GetGameTitle()
{
DebugAssert(IsOnThread());
return s_state.game_title;
}
const std::string& GPUThread::GetGameSerial()
{
DebugAssert(IsOnThread());
return s_state.game_serial;
}
void GPUThread::SetGameSerial(std::string serial)
const std::string& GPUThread::GetGamePath()
{
DebugAssert(!IsOnThread() || !s_state.use_gpu_thread);
RunOnThread([serial = std::move(serial)]() mutable {
const bool changed = (s_state.game_serial != serial);
s_state.game_serial = std::move(serial);
if (changed)
{
if (HasGPUBackend())
GPUTextureCache::GameSerialChanged();
if (SaveStateSelectorUI::IsOpen())
SaveStateSelectorUI::RefreshList();
}
});
DebugAssert(IsOnThread());
return s_state.game_path;
}
GameHash GPUThread::GetGameHash()
{
DebugAssert(IsOnThread());
return s_state.game_hash;
}

View File

@ -3,7 +3,7 @@
#pragma once
#include "common/types.h"
#include "types.h"
#include <functional>
#include <optional>
@ -46,12 +46,11 @@ void StopFullscreenUI();
/// Backend control.
std::optional<GPURenderer> GetRequestedRenderer();
bool CreateGPUBackend(std::string serial, GPURenderer renderer, bool upload_vram, bool fullscreen,
bool force_recreate_device, Error* error);
bool CreateGPUBackend(GPURenderer renderer, bool upload_vram, bool fullscreen, bool force_recreate_device,
Error* error);
void DestroyGPUBackend();
bool HasGPUBackend();
bool IsGPUBackendRequested();
void SetGameSerial(std::string serial);
/// Re-presents the current frame. Call when things like window resizes happen to re-display
/// the current frame with the correct proportions. Should only be called from the CPU thread.
@ -67,6 +66,9 @@ void ResizeDisplayWindow(s32 width, s32 height, float scale);
const WindowInfo& GetRenderWindowInfo();
void UpdateSettings(bool gpu_settings_changed, bool device_settings_changed, bool thread_changed);
void UpdateGameInfo(const std::string& title, const std::string& serial, const std::string& path, GameHash hash,
bool wake_thread = true);
void ClearGameInfo();
/// Triggers an abnormal system shutdown and waits for it to destroy the backend.
void ReportFatalErrorAndShutdown(std::string_view reason);
@ -84,7 +86,10 @@ bool GetRunIdleReason(RunIdleReason reason);
void SetRunIdleReason(RunIdleReason reason, bool enabled);
bool IsRunningIdle();
bool IsSystemPaused();
const std::string& GetGameTitle();
const std::string& GetGameSerial();
const std::string& GetGamePath();
GameHash GetGameHash();
GPUThreadCommand* AllocateCommand(GPUBackendCommandType command, u32 size);
void PushCommand(GPUThreadCommand* cmd);

View File

@ -31,6 +31,7 @@ enum class GPUBackendCommandType : u8
AsyncBackendCall,
Reconfigure,
UpdateSettings,
UpdateGameInfo,
Shutdown,
ClearVRAM,
ClearDisplay,
@ -71,7 +72,6 @@ struct GPUThreadReconfigureCommand : public GPUThreadCommand
{
Error* error_ptr;
bool* out_result;
std::string game_serial;
std::optional<GPURenderer> renderer;
std::optional<bool> fullscreen;
std::optional<bool> start_fullscreen_ui;
@ -89,6 +89,21 @@ struct GPUThreadUpdateSettingsCommand : public GPUThreadCommand
GPUSettings settings;
};
struct GPUThreadUpdateGameInfoCommand : public GPUThreadCommand
{
GPUThreadUpdateGameInfoCommand() = default;
GPUThreadUpdateGameInfoCommand(const std::string& game_title_, const std::string& game_serial_,
const std::string& game_path_, const GameHash& game_hash_)
: game_title(game_title_), game_serial(game_serial_), game_path(game_path_), game_hash(game_hash_)
{
}
std::string game_title;
std::string game_serial;
std::string game_path;
GameHash game_hash;
};
struct GPUThreadAsyncCallCommand : public GPUThreadCommand
{
GPUThreadAsyncCallCommand() = default;

View File

@ -1193,7 +1193,7 @@ void System::RecreateGPU(GPURenderer renderer)
StopMediaCapture();
Error error;
if (!GPUThread::CreateGPUBackend(s_state.running_game_serial, renderer, true, false, false, &error))
if (!GPUThread::CreateGPUBackend(renderer, true, false, false, &error))
{
ERROR_LOG("Failed to switch to {} renderer: {}", Settings::GetRendererName(renderer), error.GetDescription());
Panic("Failed to switch renderer.");
@ -1910,24 +1910,23 @@ bool System::Initialize(std::unique_ptr<CDImage> disc, DiscRegion disc_region, b
// TODO: Drop class
g_gpu.Initialize();
// Game info must be set prior to backend creation because of texture replacements.
// We don't do it in UpdateRunningGame() when booting because it can fail in a number of locations.
GPUThread::UpdateGameInfo(s_state.running_game_title, s_state.running_game_serial, s_state.running_game_path,
s_state.running_game_hash, false);
// This can fail due to the application being closed during startup.
if (!GPUThread::CreateGPUBackend(s_state.running_game_serial,
force_software_renderer ? GPURenderer::Software : g_settings.gpu_renderer, false,
if (!GPUThread::CreateGPUBackend(force_software_renderer ? GPURenderer::Software : g_settings.gpu_renderer, false,
fullscreen, false, error))
{
// Game info has to be manually cleared since the backend won't shutdown naturally.
GPUThread::ClearGameInfo();
return false;
}
if (g_settings.gpu_pgxp_enable)
CPU::PGXP::Initialize();
// Was startup cancelled? (e.g. shading compilers took too long and the user closed the application)
if (IsStartupCancelled())
{
Error::SetStringView(error, TRANSLATE_SV("System", "Startup was cancelled."));
return false;
}
DMA::Initialize();
Pad::Initialize();
Timers::Initialize();
@ -4221,15 +4220,15 @@ void System::UpdateRunningGame(const std::string& path, CDImage* image, bool boo
}
if (s_state.running_game_serial != prev_serial)
{
GPUThread::SetGameSerial(s_state.running_game_serial);
UpdateSessionTime(prev_serial);
}
UpdateRichPresence(booting);
FullscreenUI::OnRunningGameChanged(s_state.running_game_path, s_state.running_game_serial, s_state.running_game_title,
s_state.running_game_hash);
if (!booting)
{
GPUThread::UpdateGameInfo(s_state.running_game_title, s_state.running_game_serial, s_state.running_game_path,
s_state.running_game_hash);
}
Host::OnSystemGameChanged(s_state.running_game_path, s_state.running_game_serial, s_state.running_game_title,
s_state.running_game_hash);