From cb13ad7a5f27e0c11d10f06bc26a4c484f94e061 Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Tue, 17 Oct 2023 01:42:22 +0200 Subject: [PATCH] VideoCommon: Add support for icons in OSD messages. --- Source/Core/Core/Core.cpp | 10 ++-- Source/Core/VideoCommon/OnScreenDisplay.cpp | 62 ++++++++++++++++++--- Source/Core/VideoCommon/OnScreenDisplay.h | 14 ++++- 3 files changed, 73 insertions(+), 13 deletions(-) diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 5396c8439b..870d4729f7 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -579,9 +579,6 @@ static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi HW::Shutdown(system); INFO_LOG_FMT(CONSOLE, "{}", StopMessage(false, "HW shutdown")); - // Clear on screen messages that haven't expired - OSD::ClearMessages(); - // The config must be restored only after the whole HW has shut down, // not when it is still running. BootManager::RestoreConfig(); @@ -600,7 +597,12 @@ static void EmuThread(std::unique_ptr boot, WindowSystemInfo wsi PanicAlertFmt("Failed to initialize video backend!"); return; } - Common::ScopeGuard video_guard{[] { g_video_backend->Shutdown(); }}; + Common::ScopeGuard video_guard{[] { + // Clear on screen messages that haven't expired + OSD::ClearMessages(); + + g_video_backend->Shutdown(); + }}; if (cpu_info.HTT) Config::SetBaseOrCurrent(Config::MAIN_DSP_THREAD, cpu_info.num_cores > 4); diff --git a/Source/Core/VideoCommon/OnScreenDisplay.cpp b/Source/Core/VideoCommon/OnScreenDisplay.cpp index e860c53ec0..0e07dbc397 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.cpp +++ b/Source/Core/VideoCommon/OnScreenDisplay.cpp @@ -18,6 +18,10 @@ #include "Core/Config/MainSettings.h" +#include "VideoCommon/AbstractGfx.h" +#include "VideoCommon/AbstractTexture.h" +#include "VideoCommon/TextureConfig.h" + namespace OSD { constexpr float LEFT_MARGIN = 10.0f; // Pixels to the left of OSD messages. @@ -32,8 +36,8 @@ static std::atomic s_obscured_pixels_top = 0; struct Message { Message() = default; - Message(std::string text_, u32 duration_, u32 color_) - : text(std::move(text_)), duration(duration_), color(color_) + Message(std::string text_, u32 duration_, u32 color_, std::unique_ptr icon_ = nullptr) + : text(std::move(text_)), duration(duration_), color(color_), icon(std::move(icon_)) { timer.Start(); } @@ -42,7 +46,10 @@ struct Message Common::Timer timer; u32 duration = 0; bool ever_drawn = false; + bool should_discard = false; u32 color = 0; + std::unique_ptr icon; + std::unique_ptr texture; }; static std::multimap s_messages; static std::mutex s_messages_mutex; @@ -77,6 +84,33 @@ static float DrawMessage(int index, Message& msg, const ImVec2& position, int ti ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing)) { + if (msg.icon) + { + if (!msg.texture) + { + const u32 width = msg.icon->width; + const u32 height = msg.icon->height; + TextureConfig tex_config(width, height, 1, 1, 1, AbstractTextureFormat::RGBA8, 0); + msg.texture = g_gfx->CreateTexture(tex_config); + if (msg.texture) + { + msg.texture->Load(0, width, height, width, msg.icon->rgba_data.data(), + sizeof(u32) * width * height); + } + else + { + // don't try again next time + msg.icon.reset(); + } + } + + if (msg.texture) + { + ImGui::Image(msg.texture.get(), ImVec2(static_cast(msg.icon->width), + static_cast(msg.icon->height))); + } + } + // Use %s in case message contains %. ImGui::TextColored(ARGBToImVec4(msg.color), "%s", msg.text.c_str()); window_height = @@ -91,17 +125,25 @@ static float DrawMessage(int index, Message& msg, const ImVec2& position, int ti return window_height; } -void AddTypedMessage(MessageType type, std::string message, u32 ms, u32 argb) +void AddTypedMessage(MessageType type, std::string message, u32 ms, u32 argb, + std::unique_ptr icon) { std::lock_guard lock{s_messages_mutex}; - s_messages.erase(type); - s_messages.emplace(type, Message(std::move(message), ms, argb)); + + // A message may hold a reference to a texture that can only be destroyed on the video thread, so + // only mark the old typed message (if any) for removal. It will be discarded on the next call to + // DrawMessages(). + auto range = s_messages.equal_range(type); + for (auto it = range.first; it != range.second; ++it) + it->second.should_discard = true; + + s_messages.emplace(type, Message(std::move(message), ms, argb, std::move(icon))); } -void AddMessage(std::string message, u32 ms, u32 argb) +void AddMessage(std::string message, u32 ms, u32 argb, std::unique_ptr icon) { std::lock_guard lock{s_messages_mutex}; - s_messages.emplace(MessageType::Typeless, Message(std::move(message), ms, argb)); + s_messages.emplace(MessageType::Typeless, Message(std::move(message), ms, argb, std::move(icon))); } void DrawMessages() @@ -117,6 +159,12 @@ void DrawMessages() for (auto it = s_messages.begin(); it != s_messages.end();) { Message& msg = it->second; + if (msg.should_discard) + { + it = s_messages.erase(it); + continue; + } + const s64 time_left = msg.TimeRemaining(); // Make sure we draw them at least once if they were printed with 0ms, diff --git a/Source/Core/VideoCommon/OnScreenDisplay.h b/Source/Core/VideoCommon/OnScreenDisplay.h index 187c7fb317..f566eb0bf1 100644 --- a/Source/Core/VideoCommon/OnScreenDisplay.h +++ b/Source/Core/VideoCommon/OnScreenDisplay.h @@ -4,7 +4,9 @@ #pragma once #include +#include #include +#include #include "Common/CommonTypes.h" @@ -35,10 +37,18 @@ constexpr u32 NORMAL = 5000; constexpr u32 VERY_LONG = 10000; }; // namespace Duration +struct Icon +{ + std::vector rgba_data; + u32 width = 0; + u32 height = 0; +}; // struct Icon + // On-screen message display (colored yellow by default) -void AddMessage(std::string message, u32 ms = Duration::SHORT, u32 argb = Color::YELLOW); +void AddMessage(std::string message, u32 ms = Duration::SHORT, u32 argb = Color::YELLOW, + std::unique_ptr icon = nullptr); void AddTypedMessage(MessageType type, std::string message, u32 ms = Duration::SHORT, - u32 argb = Color::YELLOW); + u32 argb = Color::YELLOW, std::unique_ptr icon = nullptr); // Draw the current messages on the screen. Only call once per frame. void DrawMessages();