Achievements: Include any codepoints above 0x100 in dynamic range

This commit is contained in:
Stenzek 2025-01-31 13:48:57 +10:00
parent 03181d1179
commit b5925ab139
No known key found for this signature in database
5 changed files with 90 additions and 65 deletions

View File

@ -445,20 +445,25 @@ void Achievements::UpdateGlyphRanges()
// To avoid rasterizing all emoji fonts, we get the set of used glyphs in the emoji range for all strings in the
// current game's achievement data.
using CodepointSet = std::unordered_set<ImGuiManager::WCharType>;
CodepointSet codepoints;
CodepointSet codepoints, emoji_codepoints;
static constexpr auto add_string = [](const std::string_view str, CodepointSet& codepoints) {
const auto add_string = [&codepoints, &emoji_codepoints](const std::string_view str) {
char32_t codepoint;
for (size_t offset = 0; offset < str.length();)
{
offset += StringUtil::DecodeUTF8(str, offset, &codepoint);
// Basic Latin + Latin Supplement always included.
if (codepoint != StringUtil::UNICODE_REPLACEMENT_CHARACTER && codepoint >= 0x2000)
codepoints.insert(static_cast<ImGuiManager::WCharType>(codepoint));
if (codepoint != StringUtil::UNICODE_REPLACEMENT_CHARACTER && codepoint >= 0x100)
{
CodepointSet& dest = (codepoint >= 0x2000) ? emoji_codepoints : codepoints;
dest.insert(static_cast<ImGuiManager::WCharType>(codepoint));
}
}
};
#ifndef __ANDROID__
// We don't need to check rich presence on Android, because we're not displaying it with FullscreenUI.
if (rc_client_has_rich_presence(s_state.client))
{
std::vector<const char*> rp_strings;
@ -479,8 +484,9 @@ void Achievements::UpdateGlyphRanges()
}
for (const char* str : rp_strings)
add_string(str, codepoints);
add_string(str);
}
#endif
if (rc_client_has_achievements(s_state.client))
{
@ -495,9 +501,9 @@ void Achievements::UpdateGlyphRanges()
{
const rc_client_achievement_t* achievement = bucket.achievements[j];
if (achievement->title)
add_string(achievement->title, codepoints);
add_string(achievement->title);
if (achievement->description)
add_string(achievement->description, codepoints);
add_string(achievement->description);
}
}
rc_client_destroy_achievement_list(achievements);
@ -517,24 +523,29 @@ void Achievements::UpdateGlyphRanges()
{
const rc_client_leaderboard_t* leaderboard = bucket.leaderboards[j];
if (leaderboard->title)
add_string(leaderboard->title, codepoints);
add_string(leaderboard->title);
if (leaderboard->description)
add_string(leaderboard->description, codepoints);
add_string(leaderboard->description);
}
}
rc_client_destroy_leaderboard_list(leaderboards);
}
}
std::vector<ImGuiManager::WCharType> sorted_codepoints;
std::vector<ImGuiManager::WCharType> sorted_codepoints, sorted_emoji_codepoints;
sorted_codepoints.reserve(codepoints.size());
sorted_codepoints.insert(sorted_codepoints.begin(), codepoints.begin(), codepoints.end());
std::sort(sorted_codepoints.begin(), sorted_codepoints.end());
sorted_emoji_codepoints.reserve(codepoints.size());
sorted_emoji_codepoints.insert(sorted_emoji_codepoints.begin(), emoji_codepoints.begin(), emoji_codepoints.end());
std::sort(sorted_emoji_codepoints.begin(), sorted_emoji_codepoints.end());
// Compact codepoints to ranges.
GPUThread::RunOnThread([sorted_codepoints = std::move(sorted_codepoints)]() {
ImGuiManager::SetEmojiFontRange(ImGuiManager::CompactFontRange(sorted_codepoints));
});
GPUThread::RunOnThread(
[sorted_codepoints = std::move(sorted_codepoints), sorted_emoji_codepoints = std::move(sorted_emoji_codepoints)]() {
ImGuiManager::SetDynamicFontRange(ImGuiManager::CompactFontRange(sorted_codepoints),
ImGuiManager::CompactFontRange(sorted_emoji_codepoints));
});
}
bool Achievements::IsActive()

View File

@ -1,9 +1,10 @@
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com> and contributors.
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com> and contributors.
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
#include "mainwindow.h"
#include "qthost.h"
#include "core/gpu_thread.h"
#include "core/host.h"
#include "util/imgui_manager.h"
@ -256,9 +257,10 @@ void QtHost::UpdateGlyphRangesAndClearCache(QWidget* dialog_parent, std::string_
const char* imgui_font_url = nullptr;
std::vector<ImWchar> glyph_ranges;
glyph_ranges.clear();
glyph_ranges.reserve(std::size(s_base_latin_range) + 2);
// Base Latin range is always included.
glyph_ranges.insert(glyph_ranges.begin(), std::begin(s_base_latin_range), std::end(s_base_latin_range));
glyph_ranges.insert(glyph_ranges.end(), std::begin(s_base_latin_range), std::end(s_base_latin_range));
if (gi)
{
@ -281,7 +283,7 @@ void QtHost::UpdateGlyphRangesAndClearCache(QWidget* dialog_parent, std::string_
// If we don't have any specific glyph range, assume Central European, except if English, then keep the size down.
if ((!gi || !gi->used_glyphs) && language != "en")
{
glyph_ranges.insert(glyph_ranges.begin(), std::begin(s_central_european_ranges),
glyph_ranges.insert(glyph_ranges.end(), std::begin(s_central_european_ranges),
std::end(s_central_european_ranges));
}
@ -311,7 +313,9 @@ void QtHost::UpdateGlyphRangesAndClearCache(QWidget* dialog_parent, std::string_
if (g_emu_thread)
{
Host::RunOnCPUThread([font_path = std::move(font_path), glyph_ranges = std::move(glyph_ranges)]() mutable {
ImGuiManager::SetFontPathAndRange(std::move(font_path), std::move(glyph_ranges));
GPUThread::RunOnThread([font_path = std::move(font_path), glyph_ranges = std::move(glyph_ranges)]() mutable {
ImGuiManager::SetFontPathAndRange(std::move(font_path), std::move(glyph_ranges));
});
Host::ClearTranslationCache();
});
}

View File

@ -132,9 +132,6 @@ bool RegTestHost::InitializeConfig()
EmuFolders::LoadConfig(*s_base_settings_interface.get());
EmuFolders::EnsureFoldersExist();
// imgui setup, make sure it doesn't bug out
ImGuiManager::SetFontPathAndRange(std::string(), {0x0020, 0x00FF, 0x2022, 0x2022, 0, 0});
return true;
}

View File

@ -74,9 +74,9 @@ static void SetKeyMap();
static bool LoadFontData(Error* error);
static void ReloadFontDataIfActive();
static bool AddImGuiFonts(bool debug_font, bool fullscreen_fonts);
static ImFont* AddTextFont(float size, bool full_glyph_range);
static ImFont* AddTextFont(float size, const ImWchar* glyph_range);
static ImFont* AddFixedFont(float size);
static bool AddIconFonts(float size);
static bool AddIconFonts(float size, const ImWchar* emoji_range);
static void SetCommonIOOptions(ImGuiIO& io);
static void SetImKeyState(ImGuiIO& io, ImGuiKey imkey, bool pressed);
static const char* GetClipboardTextImpl(void* userdata);
@ -94,7 +94,8 @@ static void DrawSoftwareCursor(const SoftwareCursor& sc, const std::pair<float,
static constexpr float OSD_FADE_IN_TIME = 0.1f;
static constexpr float OSD_FADE_OUT_TIME = 0.4f;
static constexpr std::array<ImWchar, 4> s_ascii_font_range = {{0x20, 0x7F, 0x00, 0x00}};
static constexpr std::array<ImWchar, 4> ASCII_FONT_RANGE = {{0x20, 0x7F, 0x00, 0x00}};
static constexpr std::array<ImWchar, 6> DEFAULT_FONT_RANGE = {{0x0020, 0x00FF, 0x2022, 0x2022, 0x0000, 0x0000}};
namespace {
@ -140,7 +141,8 @@ struct ALIGN_TO_CACHE_LINE State
std::string font_path;
std::vector<WCharType> font_range;
std::vector<WCharType> emoji_range;
std::vector<WCharType> dynamic_font_range;
std::vector<WCharType> dynamic_emoji_range;
DynamicHeapArray<u8> standard_font_data;
DynamicHeapArray<u8> fixed_font_data;
@ -166,32 +168,13 @@ void ImGuiManager::SetFontPathAndRange(std::string path, std::vector<WCharType>
ReloadFontDataIfActive();
}
void ImGuiManager::SetEmojiFontRange(std::vector<WCharType> range)
void ImGuiManager::SetDynamicFontRange(std::vector<WCharType> font_range, std::vector<WCharType> emoji_range)
{
static constexpr size_t builtin_size = std::size(EMOJI_ICON_RANGE);
const size_t runtime_size = range.size();
if (runtime_size == 0)
{
if (s_state.emoji_range.empty())
return;
s_state.emoji_range = {};
}
else
{
if (!s_state.emoji_range.empty() && (s_state.emoji_range.size() - builtin_size) == range.size() &&
std::memcmp(s_state.emoji_range.data(), range.data(), range.size() * sizeof(ImWchar)) == 0)
{
// no change
return;
}
s_state.emoji_range = std::move(range);
s_state.emoji_range.resize(s_state.emoji_range.size() + builtin_size);
std::memcpy(&s_state.emoji_range[runtime_size], EMOJI_ICON_RANGE, sizeof(EMOJI_ICON_RANGE));
}
if (s_state.dynamic_font_range == font_range && s_state.dynamic_emoji_range == emoji_range)
return;
s_state.dynamic_font_range = std::move(font_range);
s_state.dynamic_emoji_range = std::move(emoji_range);
ReloadFontDataIfActive();
}
@ -647,13 +630,12 @@ bool ImGuiManager::LoadFontData(Error* error)
return true;
}
ImFont* ImGuiManager::AddTextFont(float size, bool full_glyph_range)
ImFont* ImGuiManager::AddTextFont(float size, const ImWchar* glyph_range)
{
ImFontConfig cfg;
cfg.FontDataOwnedByAtlas = false;
return ImGui::GetIO().Fonts->AddFontFromMemoryTTF(
s_state.standard_font_data.data(), static_cast<int>(s_state.standard_font_data.size()), size, &cfg,
full_glyph_range ? s_state.font_range.data() : s_ascii_font_range.data());
s_state.standard_font_data.data(), static_cast<int>(s_state.standard_font_data.size()), size, &cfg, glyph_range);
}
ImFont* ImGuiManager::AddFixedFont(float size)
@ -662,10 +644,10 @@ ImFont* ImGuiManager::AddFixedFont(float size)
cfg.FontDataOwnedByAtlas = false;
return ImGui::GetIO().Fonts->AddFontFromMemoryTTF(s_state.fixed_font_data.data(),
static_cast<int>(s_state.fixed_font_data.size()), size, &cfg,
s_ascii_font_range.data());
ASCII_FONT_RANGE.data());
}
bool ImGuiManager::AddIconFonts(float size)
bool ImGuiManager::AddIconFonts(float size, const ImWchar* emoji_range)
{
{
ImFontConfig cfg;
@ -708,9 +690,9 @@ bool ImGuiManager::AddIconFonts(float size)
cfg.FontDataOwnedByAtlas = false;
cfg.FontBuilderFlags = ImGuiFreeTypeBuilderFlags_LoadColor | ImGuiFreeTypeBuilderFlags_Bitmap;
if (!ImGui::GetIO().Fonts->AddFontFromMemoryTTF(
s_state.emoji_font_data.data(), static_cast<int>(s_state.emoji_font_data.size()), size * 0.9f, &cfg,
s_state.emoji_range.empty() ? EMOJI_ICON_RANGE : s_state.emoji_range.data())) [[unlikely]]
if (!ImGui::GetIO().Fonts->AddFontFromMemoryTTF(s_state.emoji_font_data.data(),
static_cast<int>(s_state.emoji_font_data.size()), size * 0.9f, &cfg,
emoji_range)) [[unlikely]]
{
return false;
}
@ -730,12 +712,43 @@ bool ImGuiManager::AddImGuiFonts(bool debug_font, bool fullscreen_fonts)
INFO_LOG("Allocating fonts winscale={} globalscale={} debug={} fullscreen={}", window_scale, s_state.global_scale,
debug_font, fullscreen_fonts);
// need to generate arrays if dynamic ranges are present
const ImWchar* text_range = s_state.font_range.empty() ? DEFAULT_FONT_RANGE.data() : s_state.font_range.data();
const ImWchar* emoji_range = EMOJI_ICON_RANGE;
std::vector<ImWchar> full_text_range, full_emoji_range;
if (!s_state.dynamic_font_range.empty())
{
// skip the zeros, we'll add them afterwards
const size_t base_size = s_state.font_range.empty() ? DEFAULT_FONT_RANGE.size() : s_state.font_range.size();
Assert(base_size > 2);
full_text_range.reserve(base_size + s_state.dynamic_font_range.size());
full_text_range.insert(full_text_range.end(), &text_range[0], &text_range[base_size - 2]);
full_text_range.insert(full_text_range.end(), s_state.dynamic_font_range.begin(), s_state.dynamic_font_range.end());
full_text_range.insert(full_text_range.end(), 2, 0);
text_range = full_text_range.data();
}
if (!s_state.dynamic_emoji_range.empty())
{
// skip the zeros, we'll add them afterwards
size_t base_size = 0;
for (const ImWchar* c = EMOJI_ICON_RANGE; *c != 0; c++)
base_size++;
Assert(base_size > 2);
full_emoji_range.reserve(base_size + s_state.dynamic_emoji_range.size());
full_emoji_range.insert(full_emoji_range.end(), &EMOJI_ICON_RANGE[0], &EMOJI_ICON_RANGE[base_size - 2]);
full_emoji_range.insert(full_emoji_range.end(), s_state.dynamic_emoji_range.begin(),
s_state.dynamic_emoji_range.end());
full_emoji_range.insert(full_emoji_range.end(), 2, 0);
emoji_range = full_emoji_range.data();
}
ImGuiIO& io = ImGui::GetIO();
io.Fonts->Clear();
if (debug_font)
{
s_state.debug_font = AddTextFont(debug_font_size, false);
s_state.debug_font = AddTextFont(debug_font_size, ASCII_FONT_RANGE.data());
if (!s_state.debug_font)
return false;
}
@ -744,8 +757,8 @@ bool ImGuiManager::AddImGuiFonts(bool debug_font, bool fullscreen_fonts)
if (!s_state.fixed_font)
return false;
s_state.osd_font = AddTextFont(osd_font_size, true);
if (!s_state.osd_font || !AddIconFonts(osd_font_size))
s_state.osd_font = AddTextFont(osd_font_size, text_range);
if (!s_state.osd_font || !AddIconFonts(osd_font_size, emoji_range))
return false;
if (!debug_font)
s_state.debug_font = s_state.osd_font;
@ -753,13 +766,13 @@ bool ImGuiManager::AddImGuiFonts(bool debug_font, bool fullscreen_fonts)
if (fullscreen_fonts)
{
const float medium_font_size = ImGuiFullscreen::LayoutScale(ImGuiFullscreen::LAYOUT_MEDIUM_FONT_SIZE);
s_state.medium_font = AddTextFont(medium_font_size, true);
if (!s_state.medium_font || !AddIconFonts(medium_font_size))
s_state.medium_font = AddTextFont(medium_font_size, text_range);
if (!s_state.medium_font || !AddIconFonts(medium_font_size, emoji_range))
return false;
const float large_font_size = ImGuiFullscreen::LayoutScale(ImGuiFullscreen::LAYOUT_LARGE_FONT_SIZE);
s_state.large_font = AddTextFont(large_font_size, true);
if (!s_state.large_font || !AddIconFonts(large_font_size))
s_state.large_font = AddTextFont(large_font_size, text_range);
if (!s_state.large_font || !AddIconFonts(large_font_size, emoji_range))
return false;
}
else

View File

@ -64,9 +64,9 @@ static constexpr float DEFAULT_SCREEN_MARGIN = 16.0f;
/// Sets the path to the font to use. Empty string means to use the default.
void SetFontPathAndRange(std::string path, std::vector<WCharType> range);
/// Sets the emoji font range to use. Empty means no glyphs will be rasterized.
/// Sets the normal/emoji font range to use. Empty means no glyphs will be rasterized.
/// Should NOT be terminated with zeros, unlike the font range above.
void SetEmojiFontRange(std::vector<WCharType> range);
void SetDynamicFontRange(std::vector<WCharType> font_range, std::vector<WCharType> emoji_range);
/// Returns a compacted font range, with adjacent glyphs merged into one pair.
std::vector<WCharType> CompactFontRange(std::span<const WCharType> range);