FullscreenUI: Always initialize widgets

That way notifications etc can be used independently of FSUI.
pull/3589/head
Stenzek 6 days ago
parent 9cbcf78090
commit 8c25144abb
No known key found for this signature in database

@ -1317,11 +1317,6 @@ void Achievements::ClientLoadGameCallback(int result, const char* error_message,
s_state.has_achievements = has_achievements;
s_state.has_leaderboards = has_leaderboards;
s_state.has_rich_presence = rc_client_has_rich_presence(client);
// ensure fullscreen UI is ready for notifications
if (display_summary)
GPUThread::RunOnThread(&FullscreenUI::Initialize);
s_state.game_icon_url =
info->badge_url ? std::string(info->badge_url) : GetImageURL(info->badge_name, RC_IMAGE_TYPE_GAME);
s_state.game_icon = GetLocalImagePath(info->badge_name, RC_IMAGE_TYPE_GAME);
@ -1410,18 +1405,12 @@ void Achievements::DisplayAchievementSummary()
GPUThread::RunOnThread([title = s_state.game_title, summary = std::string(summary.view()), icon = s_state.game_icon,
time = IsHardcoreModeActive() ? ACHIEVEMENT_SUMMARY_NOTIFICATION_TIME_HC :
ACHIEVEMENT_SUMMARY_NOTIFICATION_TIME]() mutable {
if (!FullscreenUI::Initialize())
return;
FullscreenUI::AddNotification("AchievementsSummary", time, std::move(title), std::move(summary), std::move(icon));
});
if (s_state.game_summary.num_unsupported_achievements > 0)
{
GPUThread::RunOnThread([num_unsupported = s_state.game_summary.num_unsupported_achievements]() mutable {
if (!FullscreenUI::Initialize())
return;
FullscreenUI::AddNotification("UnsupportedAchievements", ACHIEVEMENT_SUMMARY_UNSUPPORTED_TIME,
TRANSLATE_STR("Achievements", "Unsupported Achievements"),
TRANSLATE_PLURAL_STR("Achievements",
@ -1442,9 +1431,6 @@ void Achievements::DisplayHardcoreDeferredMessage()
if (g_settings.achievements_hardcore_mode && System::IsValid())
{
GPUThread::RunOnThread([]() {
if (!FullscreenUI::Initialize())
return;
FullscreenUI::ShowToast(std::string(),
TRANSLATE_STR("Achievements", "Hardcore mode will be enabled on system reset."),
Host::OSD_WARNING_DURATION);
@ -1478,9 +1464,6 @@ void Achievements::HandleUnlockEvent(const rc_client_event_t* event)
GPUThread::RunOnThread([id = cheevo->id, duration = g_settings.achievements_notification_duration,
title = std::move(title), description = std::string(cheevo->description),
badge_path = std::move(badge_path)]() mutable {
if (!FullscreenUI::Initialize())
return;
FullscreenUI::AddNotification(fmt::format("achievement_unlock_{}", id), static_cast<float>(duration),
std::move(title), std::move(description), std::move(badge_path));
});
@ -1505,9 +1488,6 @@ void Achievements::HandleGameCompleteEvent(const rc_client_event_t* event)
GPUThread::RunOnThread(
[title = s_state.game_title, message = std::move(message), icon = s_state.game_icon]() mutable {
if (!FullscreenUI::Initialize())
return;
FullscreenUI::AddNotification("achievement_mastery", GAME_COMPLETE_NOTIFICATION_TIME, std::move(title),
std::move(message), std::move(icon));
});
@ -1542,9 +1522,6 @@ void Achievements::HandleSubsetCompleteEvent(const rc_client_event_t* event)
GPUThread::RunOnThread(
[title = std::move(title), message = std::move(message), badge_path = std::move(badge_path)]() mutable {
if (!FullscreenUI::Initialize())
return;
FullscreenUI::AddNotification("achievement_mastery", GAME_COMPLETE_NOTIFICATION_TIME, std::move(title),
std::move(message), std::move(badge_path));
});
@ -1562,9 +1539,6 @@ void Achievements::HandleLeaderboardStartedEvent(const rc_client_event_t* event)
GPUThread::RunOnThread([id = event->leaderboard->id, title = std::move(title), message = std::move(message),
icon = s_state.game_icon]() mutable {
if (!FullscreenUI::Initialize())
return;
FullscreenUI::AddNotification(fmt::format("leaderboard_{}", id), LEADERBOARD_STARTED_NOTIFICATION_TIME,
std::move(title), std::move(message), std::move(icon));
});
@ -1582,9 +1556,6 @@ void Achievements::HandleLeaderboardFailedEvent(const rc_client_event_t* event)
GPUThread::RunOnThread([id = event->leaderboard->id, title = std::move(title), message = std::move(message),
icon = s_state.game_icon]() mutable {
if (!FullscreenUI::Initialize())
return;
FullscreenUI::AddNotification(fmt::format("leaderboard_{}", id), LEADERBOARD_FAILED_NOTIFICATION_TIME,
std::move(title), std::move(message), std::move(icon));
});
@ -1613,8 +1584,6 @@ void Achievements::HandleLeaderboardSubmittedEvent(const rc_client_event_t* even
GPUThread::RunOnThread([id = event->leaderboard->id, title = std::move(title), message = std::move(message),
icon = s_state.game_icon]() mutable {
if (!FullscreenUI::Initialize())
return;
FullscreenUI::AddNotification(fmt::format("leaderboard_{}", id),
static_cast<float>(g_settings.achievements_leaderboard_duration), std::move(title),
std::move(message), std::move(icon));
@ -1649,9 +1618,6 @@ void Achievements::HandleLeaderboardScoreboardEvent(const rc_client_event_t* eve
GPUThread::RunOnThread([id = event->leaderboard->id, title = std::move(title), message = std::move(message),
icon = s_state.game_icon]() mutable {
if (!FullscreenUI::Initialize())
return;
FullscreenUI::AddNotification(fmt::format("leaderboard_{}", id),
static_cast<float>(g_settings.achievements_leaderboard_duration), std::move(title),
std::move(message), std::move(icon));
@ -1734,9 +1700,6 @@ void Achievements::HandleAchievementChallengeIndicatorShowEvent(const rc_client_
[title = std::move(title),
description = std::string(event->achievement->description ? event->achievement->description : ""), badge_path,
id = event->achievement->id]() mutable {
if (!FullscreenUI::Initialize())
return;
FullscreenUI::AddNotification(fmt::format("AchievementChallenge{}", id), CHALLENGE_STARTED_NOTIFICATION_TIME,
std::move(title), std::move(description), std::move(badge_path));
});
@ -1772,9 +1735,6 @@ void Achievements::HandleAchievementChallengeIndicatorHideEvent(const rc_client_
[title = std::move(title),
description = std::string(event->achievement->description ? event->achievement->description : ""),
badge_path = std::move(badge_path), id = event->achievement->id]() mutable {
if (!FullscreenUI::Initialize())
return;
FullscreenUI::AddNotification(fmt::format("AchievementChallenge{}", id), CHALLENGE_FAILED_NOTIFICATION_TIME,
std::move(title), std::move(description), std::move(badge_path));
});
@ -1851,9 +1811,6 @@ void Achievements::HandleServerDisconnectedEvent(const rc_client_event_t* event)
WARNING_LOG("Server disconnected.");
GPUThread::RunOnThread([]() {
if (!FullscreenUI::Initialize())
return;
FullscreenUI::ShowToast(
TRANSLATE_STR("Achievements", "Achievements Disconnected"),
TRANSLATE_STR("Achievements",
@ -1867,9 +1824,6 @@ void Achievements::HandleServerReconnectedEvent(const rc_client_event_t* event)
WARNING_LOG("Server reconnected.");
GPUThread::RunOnThread([]() {
if (!FullscreenUI::Initialize())
return;
FullscreenUI::ShowToast(TRANSLATE_STR("Achievements", "Achievements Reconnected"),
TRANSLATE_STR("Achievements", "All pending unlock requests have completed."),
Host::OSD_INFO_DURATION);
@ -1906,9 +1860,6 @@ void Achievements::OnHardcoreModeChanged(bool enabled, bool display_message, boo
if (System::IsValid() && display_message)
{
GPUThread::RunOnThread([enabled]() {
if (!FullscreenUI::Initialize())
return;
FullscreenUI::ShowToast(std::string(),
enabled ? TRANSLATE_STR("Achievements", "Hardcore mode is now enabled.") :
TRANSLATE_STR("Achievements", "Hardcore mode is now disabled."),
@ -2202,7 +2153,8 @@ void Achievements::ClientLoginWithTokenCallback(int result, const char* error_me
TRANSLATE_FS("Achievements", "Achievement unlocks will not be submitted for this session.\nError: {}"),
error_message);
GPUThread::RunOnThread([message = std::move(message)]() mutable {
if (!GPUThread::HasGPUBackend() || !FullscreenUI::Initialize())
// Only display this notification if we're booting a game.
if (!GPUThread::HasGPUBackend())
return;
FullscreenUI::AddNotification("AchievementsLoginFailed", Host::OSD_ERROR_DURATION,
@ -2252,9 +2204,6 @@ void Achievements::ShowLoginNotification()
GPUThread::RunOnThread(
[title = std::move(title), summary = std::move(summary), badge_path = std::move(badge_path)]() mutable {
if (!FullscreenUI::Initialize())
return;
FullscreenUI::AddNotification("achievements_login", LOGIN_NOTIFICATION_TIME, std::move(title),
std::move(summary), std::move(badge_path));
});

@ -393,7 +393,6 @@ struct ALIGN_TO_CACHE_LINE WidgetsState
PauseSubMenu current_pause_submenu = PauseSubMenu::None;
MainWindowType previous_main_window = MainWindowType::None;
bool initialized = false;
bool tried_to_initialize = false;
bool pause_menu_was_open = false;
bool was_paused_on_quick_menu_open = false;
std::string achievements_user_badge_path;
@ -464,20 +463,11 @@ static WidgetsState s_state;
// Main
//////////////////////////////////////////////////////////////////////////
bool FullscreenUI::Initialize()
void FullscreenUI::Initialize()
{
if (s_state.initialized)
return true;
// some achievement callbacks fire early while e.g. there is a load state popup blocking system init
if (s_state.tried_to_initialize || !ImGuiManager::IsInitialized())
return false;
if (!InitializeWidgets())
{
s_state.tried_to_initialize = true;
return false;
}
if (s_state.initialized || !ImGuiManager::IsInitialized())
return;
s_state.initialized = true;
s_state.show_localized_titles = Host::GetBaseBoolSettingValue("Main", "FullscreenUIShowLocalizedTitles", true);
@ -496,8 +486,6 @@ bool FullscreenUI::Initialize()
{
UpdateRunIdleState();
}
return true;
}
bool FullscreenUI::IsInitialized()
@ -611,7 +599,8 @@ void FullscreenUI::OpenPauseMenu()
return;
GPUThread::RunOnThread([]() {
if (!Initialize() || s_state.current_main_window != MainWindowType::None)
Initialize();
if (s_state.current_main_window != MainWindowType::None)
return;
PauseForMenuOpen(true);
@ -631,7 +620,8 @@ void FullscreenUI::OpenCheatsMenu()
return;
GPUThread::RunOnThread([]() {
if (!Initialize() || s_state.current_main_window != MainWindowType::None)
Initialize();
if (s_state.current_main_window != MainWindowType::None)
return;
PauseForMenuOpen(false);
@ -798,10 +788,8 @@ void FullscreenUI::Shutdown(bool clear_state)
}
DestroyResources();
ShutdownWidgets(clear_state);
s_state.initialized = false;
s_state.tried_to_initialize = false;
UpdateRunIdleState();
}
@ -1252,8 +1240,7 @@ void FullscreenUI::BeginChangeDiscOnCPUThread(bool needs_pause)
options.emplace_back(System::GetMediaSubImageTitle(i), i == current_index);
GPUThread::RunOnThread([options = std::move(options), pause_if_needed = std::move(pause_if_needed)]() mutable {
if (!Initialize())
return;
Initialize();
auto callback = [](s32 index, const std::string& title, bool checked) {
if (index == 0)
@ -1304,8 +1291,7 @@ void FullscreenUI::BeginChangeDiscOnCPUThread(bool needs_pause)
GPUThread::RunOnThread([options = std::move(options), paths = std::move(paths),
pause_if_needed = std::move(pause_if_needed)]() mutable {
if (!Initialize())
return;
Initialize();
auto callback = [paths = std::move(paths)](s32 index, const std::string& title, bool checked) mutable {
if (index == 0)
@ -1337,9 +1323,7 @@ void FullscreenUI::BeginChangeDiscOnCPUThread(bool needs_pause)
}
GPUThread::RunOnThread([pause_if_needed = std::move(pause_if_needed)]() {
if (!Initialize())
return;
Initialize();
StartChangeDiscFromFile();
pause_if_needed();
});
@ -8721,9 +8705,6 @@ void FullscreenUI::OpenAchievementsWindow()
if (!Achievements::IsActive() || !Achievements::HasAchievements())
{
GPUThread::RunOnThread([]() {
if (!Initialize())
return;
ShowToast(std::string(), Achievements::IsActive() ? FSUI_STR("This game has no achievements.") :
FSUI_STR("Achievements are not enabled."));
});
@ -8731,8 +8712,7 @@ void FullscreenUI::OpenAchievementsWindow()
}
GPUThread::RunOnThread([]() {
if (!Initialize())
return;
Initialize();
PauseForMenuOpen(false);
ForceKeyNavEnabled();
@ -8761,9 +8741,6 @@ void FullscreenUI::OpenLeaderboardsWindow()
if (!Achievements::IsActive() || !Achievements::HasLeaderboards())
{
GPUThread::RunOnThread([]() {
if (!Initialize())
return;
ShowToast(std::string(), Achievements::IsActive() ? FSUI_STR("This game has no leaderboards.") :
FSUI_STR("Achievements are not enabled."));
});
@ -8771,8 +8748,7 @@ void FullscreenUI::OpenLeaderboardsWindow()
}
GPUThread::RunOnThread([]() {
if (!Initialize())
return;
Initialize();
PauseForMenuOpen(false);
ForceKeyNavEnabled();

@ -19,7 +19,7 @@ class SmallStringBase;
struct GPUSettings;
namespace FullscreenUI {
bool Initialize();
void Initialize();
bool IsInitialized();
bool HasActiveWindow();
void CheckForConfigChanges(const GPUSettings& old_settings);

@ -57,7 +57,7 @@ static constexpr u32 LOADING_PROGRESS_SAMPLE_COUNT = 30;
static std::optional<Image> LoadTextureImage(std::string_view path, u32 svg_width, u32 svg_height);
static std::shared_ptr<GPUTexture> UploadTexture(std::string_view path, const Image& image);
static bool CompileTransitionPipelines();
static bool CompileTransitionPipelines(Error* error);
static void CreateFooterTextString(SmallStringBase& dest,
std::span<const std::pair<const char*, std::string_view>> items);
@ -374,15 +374,16 @@ void FullscreenUI::SetFont(ImFont* ui_font)
UIStyle.Font = ui_font;
}
bool FullscreenUI::InitializeWidgets()
bool FullscreenUI::InitializeWidgets(Error* error)
{
std::unique_lock lock(s_state.shared_state_mutex);
s_state.focus_reset_queued = FocusResetType::ViewChanged;
s_state.close_button_state = CloseButtonState::None;
s_state.placeholder_texture = LoadTexture("images/placeholder.png");
if (!s_state.placeholder_texture || !CompileTransitionPipelines())
if (!(s_state.placeholder_texture = LoadTexture("images/placeholder.png")))
Error::SetStringView(error, "Failed to load placeholder.png");
if (!s_state.placeholder_texture || !CompileTransitionPipelines(error))
{
ShutdownWidgets(true);
return false;
@ -741,20 +742,19 @@ GPUTexture* FullscreenUI::GetTransitionRenderTexture(GPUSwapChain* swap_chain)
return s_state.transition_current_texture.get();
}
bool FullscreenUI::CompileTransitionPipelines()
bool FullscreenUI::CompileTransitionPipelines(Error* error)
{
const RenderAPI render_api = g_gpu_device->GetRenderAPI();
const ShaderGen shadergen(render_api, ShaderGen::GetShaderLanguageForAPI(render_api), false, false);
GPUSwapChain* const swap_chain = g_gpu_device->GetMainSwapChain();
Error error;
std::unique_ptr<GPUShader> vs = g_gpu_device->CreateShader(GPUShaderStage::Vertex, shadergen.GetLanguage(),
shadergen.GeneratePassthroughVertexShader(), &error);
shadergen.GeneratePassthroughVertexShader(), error);
std::unique_ptr<GPUShader> fs = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateFadeFragmentShader(), &error);
shadergen.GenerateFadeFragmentShader(), error);
if (!vs || !fs)
{
ERROR_LOG("Failed to compile transition shaders: {}", error.GetDescription());
Error::AddPrefix(error, "Failed to compile transition shaders: ");
return false;
}
GL_OBJECT_NAME(vs, "Transition Vertex Shader");
@ -774,10 +774,10 @@ bool FullscreenUI::CompileTransitionPipelines()
plconfig.geometry_shader = nullptr;
plconfig.fragment_shader = fs.get();
s_state.transition_blend_pipeline = g_gpu_device->CreatePipeline(plconfig, &error);
s_state.transition_blend_pipeline = g_gpu_device->CreatePipeline(plconfig, error);
if (!s_state.transition_blend_pipeline)
{
ERROR_LOG("Failed to create transition blend pipeline: {}", error.GetDescription());
Error::AddPrefix(error, "Failed to create transition blend pipeline: ");
return false;
}
@ -4627,7 +4627,7 @@ void FullscreenUI::AddNotification(std::string key, float duration, std::string
notif.target_y = -1.0f;
notif.last_y = -1.0f;
s_state.notifications.push_back(std::move(notif));
FullscreenUI::UpdateRunIdleState();
UpdateRunIdleState();
}
bool FullscreenUI::HasAnyNotifications()

@ -23,6 +23,7 @@
#include <utility>
#include <vector>
class Error;
class Image;
class GPUTexture;
class GPUSwapChain;
@ -208,7 +209,7 @@ ImRect CenterImage(const ImRect& fit_rect, const GPUTexture* texture);
ImRect FitImage(const ImVec2& fit_size, const ImVec2& image_size);
/// Initializes, setting up any state.
bool InitializeWidgets();
bool InitializeWidgets(Error* error);
/// Shuts down, clearing all state.
void ShutdownWidgets(bool clear_state);

@ -1087,8 +1087,7 @@ bool GPUPresenter::PresentFrame(GPUPresenter* presenter, GPUBackend* backend, bo
ImGuiManager::RenderOSDMessages();
if (FullscreenUI::IsInitialized())
FullscreenUI::RenderOverlays();
FullscreenUI::RenderOverlays();
if (backend && !GPUThread::IsSystemPaused())
ImGuiManager::RenderSoftwareCursors();

@ -696,13 +696,11 @@ bool GPUThread::CreateDeviceOnThread(RenderAPI api, bool fullscreen, bool clear_
}
if (!ImGuiManager::Initialize(g_gpu_settings.display_osd_scale / 100.0f, g_gpu_settings.display_osd_margin,
&create_error) ||
(s_state.requested_fullscreen_ui && !FullscreenUI::Initialize()))
&create_error))
{
ERROR_LOG("Failed to initialize ImGuiManager: {}", create_error.GetDescription());
Error::SetStringFmt(error, "Failed to initialize ImGuiManager: {}", create_error.GetDescription());
FullscreenUI::Shutdown(clear_fsui_state_on_failure);
ImGuiManager::Shutdown();
ImGuiManager::Shutdown(clear_fsui_state_on_failure);
g_gpu_device->Destroy();
g_gpu_device.reset();
if (wi.has_value())
@ -710,6 +708,9 @@ bool GPUThread::CreateDeviceOnThread(RenderAPI api, bool fullscreen, bool clear_
return false;
}
if (s_state.requested_fullscreen_ui)
FullscreenUI::Initialize();
InputManager::SetDisplayWindowSize(ImGuiManager::GetWindowWidth(), ImGuiManager::GetWindowHeight());
if (const GPUSwapChain* swap_chain = g_gpu_device->GetMainSwapChain())
@ -736,7 +737,7 @@ void GPUThread::DestroyDeviceOnThread(bool clear_fsui_state)
Assert(!s_state.gpu_presenter);
FullscreenUI::Shutdown(clear_fsui_state);
ImGuiManager::Shutdown();
ImGuiManager::Shutdown(clear_fsui_state);
INFO_LOG("Destroying {} GPU device...", GPUDevice::RenderAPIToString(g_gpu_device->GetRenderAPI()));
g_gpu_device->Destroy();
@ -910,10 +911,9 @@ void GPUThread::ReconfigureOnThread(GPUThreadReconfigureCommand* cmd)
}
else if (s_state.requested_fullscreen_ui)
{
const bool had_gpu_device = static_cast<bool>(g_gpu_device);
if (!g_gpu_device && !CreateDeviceOnThread(expected_api, cmd->fullscreen.value_or(false), true, cmd->error_ptr))
if (!(*cmd->out_result =
g_gpu_device || CreateDeviceOnThread(expected_api, cmd->fullscreen.value_or(false), true, cmd->error_ptr)))
{
*cmd->out_result = false;
return;
}
@ -924,12 +924,8 @@ void GPUThread::ReconfigureOnThread(GPUThreadReconfigureCommand* cmd)
// Don't need timing to run FSUI.
g_gpu_device->SetGPUTimingEnabled(false);
if (!(*cmd->out_result = FullscreenUI::IsInitialized() || FullscreenUI::Initialize()))
{
Error::SetStringView(cmd->error_ptr, "Failed to initialize FullscreenUI.");
if (!had_gpu_device)
DestroyDeviceOnThread(true);
}
// Ensure FSUI is initialized.
FullscreenUI::Initialize();
}
}

@ -2151,12 +2151,6 @@ void Host::ConfirmMessageAsync(std::string_view title, std::string_view message,
callback(result);
};
if (!FullscreenUI::Initialize())
{
final_callback(false);
return;
}
FullscreenUI::OpenConfirmMessageDialog(std::move(title), std::move(message), std::move(final_callback),
fmt::format(ICON_FA_CHECK " {}", yes_text),
fmt::format(ICON_FA_XMARK " {}", no_text));

@ -225,7 +225,7 @@ bool ImGuiManager::Initialize(float global_scale, float screen_margin, Error* er
FullscreenUI::UpdateTheme();
FullscreenUI::UpdateLayoutScale();
if (!CreateFontAtlas(error) || !CompilePipelines(error))
if (!CreateFontAtlas(error) || !CompilePipelines(error) || !FullscreenUI::InitializeWidgets(error))
return false;
NewFrame();
@ -234,10 +234,12 @@ bool ImGuiManager::Initialize(float global_scale, float screen_margin, Error* er
return true;
}
void ImGuiManager::Shutdown()
void ImGuiManager::Shutdown(bool clear_fsui_state)
{
DestroySoftwareCursorTextures();
FullscreenUI::ShutdownWidgets(clear_fsui_state);
s_state.text_font = nullptr;
s_state.fixed_font = nullptr;
FullscreenUI::SetFont(nullptr);

@ -87,7 +87,7 @@ void SetGlobalScale(float global_scale);
bool Initialize(float global_scale, float screen_margin, Error* error);
/// Frees all ImGui resources.
void Shutdown();
void Shutdown(bool clear_fsui_state);
/// Returns main ImGui context.
ImGuiContext* GetMainContext();

Loading…
Cancel
Save