feat: 支持格鲁吉亚语和构建 ImGui 字体的性能优化

This commit is contained in:
Xu 2025-03-22 17:03:54 +08:00
commit 1e4df90075
14 changed files with 98 additions and 67 deletions

View file

@ -10,14 +10,14 @@
namespace yas::detail {
// winrt::com_ptr<ID3DBlob>
template<std::size_t F>
template <std::size_t F>
struct serializer<
type_prop::not_a_fundamental,
ser_case::use_internal_serializer,
F,
winrt::com_ptr<ID3DBlob>
> {
template<typename Archive>
template <typename Archive>
static Archive& save(Archive& ar, const winrt::com_ptr<ID3DBlob>& blob) {
uint32_t size = (uint32_t)blob->GetBufferSize();
ar& size;
@ -27,7 +27,7 @@ struct serializer<
return ar;
}
template<typename Archive>
template <typename Archive>
static Archive& load(Archive& ar, winrt::com_ptr<ID3DBlob>& blob) {
uint32_t size = 0;
ar& size;
@ -47,27 +47,27 @@ struct serializer<
namespace Magpie {
template<typename Archive>
template <typename Archive>
void serialize(Archive& ar, EffectParameterDesc& o) {
ar& o.name& o.label& o.constant;
}
template<typename Archive>
template <typename Archive>
void serialize(Archive& ar, EffectIntermediateTextureDesc& o) {
ar& o.format& o.name& o.source& o.sizeExpr;
}
template<typename Archive>
template <typename Archive>
void serialize(Archive& ar, EffectSamplerDesc& o) {
ar& o.filterType& o.addressType& o.name;
}
template<typename Archive>
template <typename Archive>
void serialize(Archive& ar, EffectPassDesc& o) {
ar& o.cso& o.inputs& o.outputs& o.numThreads[0] & o.numThreads[1] & o.numThreads[2] & o.blockSize& o.desc& o.flags;
}
template<typename Archive>
template <typename Archive>
void serialize(Archive& ar, EffectDesc& o) {
ar& o.name& o.params& o.textures& o.samplers& o.passes& o.flags;
}

View file

@ -118,7 +118,7 @@ static uint32_t RemoveComments(std::string& source) noexcept {
return 0;
}
template<bool IncludeNewLine>
template <bool IncludeNewLine>
static void RemoveLeadingBlanks(std::string_view& source) noexcept {
size_t i = 0;
for (; i < source.size(); ++i) {
@ -137,7 +137,7 @@ static void RemoveLeadingBlanks(std::string_view& source) noexcept {
source.remove_prefix(i);
}
template<bool AllowNewLine>
template <bool AllowNewLine>
static bool CheckNextToken(std::string_view& source, std::string_view token) noexcept {
RemoveLeadingBlanks<AllowNewLine>(source);
@ -149,7 +149,7 @@ static bool CheckNextToken(std::string_view& source, std::string_view token) noe
return true;
}
template<bool AllowNewLine>
template <bool AllowNewLine>
static uint32_t GetNextToken(std::string_view& source, std::string_view& value) noexcept {
RemoveLeadingBlanks<AllowNewLine>(source);
@ -224,7 +224,7 @@ static uint32_t GetNextString(std::string_view& source, std::string_view& value)
return 0;
}
template<typename T>
template <typename T>
static uint32_t GetNextNumber(std::string_view& source, T& value) noexcept {
RemoveLeadingBlanks<false>(source);

View file

@ -51,7 +51,7 @@ void EffectsProfiler::OnEndEffects(ID3D11DeviceContext* d3dDC) {
d3dDC->End(_disjointQuery.get());
}
template<typename T>
template <typename T>
static T GetQueryData(ID3D11DeviceContext* d3dDC, ID3D11Query* query) noexcept {
T data{};
while (d3dDC->GetData(query, &data, sizeof(data), 0) != S_OK) {

View file

@ -10,14 +10,14 @@
namespace yas::detail {
// ImVector
template<std::size_t F, typename T>
template <std::size_t F, typename T>
struct serializer<
type_prop::not_a_fundamental,
ser_case::use_internal_serializer,
F,
ImVector<T>
> {
template<typename Archive>
template <typename Archive>
static Archive& save(Archive& ar, const ImVector<T>& vector) noexcept {
uint32_t size = (uint32_t)vector.size();
ar& size;
@ -33,7 +33,7 @@ struct serializer<
return ar;
}
template<typename Archive>
template <typename Archive>
static Archive& load(Archive& ar, ImVector<T>& vector) noexcept {
uint32_t size = 0;
ar& size;
@ -52,14 +52,14 @@ struct serializer<
};
// 对 ImFontAtlas 的序列化与反序列化来自 https://github.com/ocornut/imgui/issues/6169
template<std::size_t F>
template <std::size_t F>
struct serializer<
type_prop::not_a_fundamental,
ser_case::use_internal_serializer,
F,
ImFontAtlas
> {
template<typename Archive>
template <typename Archive>
static Archive& save(Archive& ar, const ImFontAtlas& fontAltas) noexcept {
ar& fontAltas.Flags & fontAltas.TexUvWhitePixel& fontAltas.TexUvLines;
@ -85,7 +85,7 @@ struct serializer<
return ar;
}
template<typename Archive>
template <typename Archive>
static Archive& load(Archive& ar, ImFontAtlas& fontAltas) noexcept {
fontAltas.ClearTexData();
ar& fontAltas.Flags& fontAltas.TexUvWhitePixel& fontAltas.TexUvLines;

View file

@ -10,13 +10,18 @@ struct ImGuiHelper {
static constexpr ImWchar NUMBER_RANGES[] = { L'0', L'9', 0 };
static constexpr ImWchar NOT_NUMBER_RANGES[] = { 0x20, L'0' - 1, L'9' + 1, 0x7E, 0 };
// Basic Latin
static constexpr ImWchar ENGLISH_RANGES[] = { 0x20, 0x7E, 0 };
static constexpr ImWchar BASIC_LATIN_RANGES[] = { 0x20, 0x7E, 0 };
// Basic Latin + Latin-1 Supplement + Latin Extended-A用于土耳其语、匈牙利语等。
// 参见 https://en.wikipedia.org/wiki/Latin_Extended-A
static constexpr ImWchar Latin_1_Extended_A_RANGES[] = { 0x20, 0x17F, 0 };
static constexpr ImWchar EXTENDED_LATIN_RANGES[] = { 0x20, 0x17F, 0 };
// Basic Latin + Georgian + Georgian Supplement + Georgian Extended用于格鲁吉亚语。
// https://en.wikipedia.org/wiki/Georgian_scripts
static constexpr ImWchar GEORGIAN_RANGES[] = { 0x20, 0x7E, 0x10A0, 0x10FF, 0x2D00, 0x2D2F, 0x1C90, 0x1CBF, 0 };
// 不包含 Basic Latin用于 Win11
static constexpr ImWchar GEORGIAN_EXTRA_RANGES[] = { 0x10A0, 0x10FF, 0x2D00, 0x2D2F, 0x1C90, 0x1CBF, 0 };
// Tamil用于泰米尔语。Tamil Supplement 超出了 ImWchar16 的存储范围,因此暂不支持。
// 参见 https://en.wikipedia.org/wiki/Tamil_script
static constexpr ImWchar TAMIL_RANGES[] = { 0xB80, 0xBFF, 0 };
static constexpr ImWchar TAMIL_EXTRA_RANGES[] = { 0xB80, 0xBFF, 0 };
};
}

View file

@ -353,7 +353,7 @@ bool OverlayDrawer::_BuildFonts() noexcept {
{
// 构建 ImFontAtlas 前 uiRanges 不能析构,因为 ImGui 只保存了指针
ImVector<ImWchar> uiRanges;
std::vector<ImWchar> uiRanges;
_BuildFontUI(language, fontData, uiRanges);
_BuildFontFPS(fontData);
@ -393,31 +393,45 @@ bool OverlayDrawer::_BuildFonts() noexcept {
return true;
}
template <size_t SIZE>
static void SetGlyphRanges(std::vector<ImWchar>& uiRanges, const ImWchar (&ranges)[SIZE]) noexcept {
uiRanges.assign(std::begin(ranges), std::end(ranges));
}
// 指针重载,但不能直接使用指针
template <typename T, typename = std::enable_if_t<std::is_same_v<T, const ImWchar*>>>
static void SetGlyphRanges(std::vector<ImWchar>& uiRanges, T ranges) noexcept {
for (const ImWchar* range = ranges; *range; ++range) {
uiRanges.push_back(*range);
}
}
void OverlayDrawer::_BuildFontUI(
std::wstring_view language,
const std::vector<uint8_t>& fontData,
ImVector<ImWchar>& uiRanges
std::vector<ImWchar>& uiRanges
) noexcept {
ImFontAtlas& fontAtlas = *ImGui::GetIO().Fonts;
std::string extraFontPath;
const ImWchar* extraRanges = nullptr;
int extraFontNo = 0;
ImFontGlyphRangesBuilder builder;
assert(uiRanges.empty());
if (language == L"en-us") {
builder.AddRanges(ImGuiHelper::ENGLISH_RANGES);
SetGlyphRanges(uiRanges, ImGuiHelper::BASIC_LATIN_RANGES);
} else if (language == L"ru" || language == L"uk") {
builder.AddRanges(fontAtlas.GetGlyphRangesCyrillic());
SetGlyphRanges(uiRanges, fontAtlas.GetGlyphRangesCyrillic());
} else if (language == L"tr" || language == L"hu" || language == L"pl") {
builder.AddRanges(ImGuiHelper::Latin_1_Extended_A_RANGES);
SetGlyphRanges(uiRanges, ImGuiHelper::EXTENDED_LATIN_RANGES);
} else if (language == L"vi") {
builder.AddRanges(fontAtlas.GetGlyphRangesVietnamese());
SetGlyphRanges(uiRanges, fontAtlas.GetGlyphRangesVietnamese());
} else if (language == L"ka" && !Win32Helper::GetOSVersion().IsWin11()) {
// Win10 中格鲁吉亚语无需加载额外字体
SetGlyphRanges(uiRanges, ImGuiHelper::GEORGIAN_RANGES);
} else {
// 默认 Basic Latin + Latin-1 Supplement
// 参见 https://en.wikipedia.org/wiki/Latin-1_Supplement
builder.AddRanges(fontAtlas.GetGlyphRangesDefault());
// Basic Latin 使用默认字体
SetGlyphRanges(uiRanges, ImGuiHelper::BASIC_LATIN_RANGES);
// 一些语言需要加载额外的字体:
// 简体中文 -> Microsoft YaHei UI
@ -425,6 +439,7 @@ void OverlayDrawer::_BuildFontUI(
// 日语 -> Yu Gothic UI
// 韩语/朝鲜语 -> Malgun Gothic
// 泰米尔语 -> Nirmala UI
// 格鲁吉亚语 -> Segoe UI (仅限 Win11)
// 参见 https://learn.microsoft.com/en-us/windows/apps/design/style/typography#fonts-for-non-latin-languages
if (language == L"zh-hans") {
// msyh.ttc: 0 是微软雅黑1 是 Microsoft YaHei UI
@ -441,16 +456,22 @@ void OverlayDrawer::_BuildFontUI(
extraFontPath = StrHelper::Concat(StrHelper::UTF16ToUTF8(GetSystemFontsFolder()), "\\YuGothM.ttc");
extraFontNo = 1;
extraRanges = fontAtlas.GetGlyphRangesJapanese();
} else if (language == L"ka") {
// Win11 中的 Segoe UI Variable 不包含格鲁吉亚字母,需额外加载 Segoe UI
extraFontPath = StrHelper::Concat(StrHelper::UTF16ToUTF8(GetSystemFontsFolder()), "\\segoeui.ttf");
extraRanges = ImGuiHelper::GEORGIAN_EXTRA_RANGES;
} else if (language == L"ko") {
extraFontPath = StrHelper::Concat(StrHelper::UTF16ToUTF8(GetSystemFontsFolder()), "\\malgun.ttf");
extraRanges = fontAtlas.GetGlyphRangesKorean();
} else if (language == L"ta") {
extraFontPath = StrHelper::Concat(StrHelper::UTF16ToUTF8(GetSystemFontsFolder()), "\\Nirmala.ttf");
extraRanges = ImGuiHelper::TAMIL_RANGES;
extraRanges = ImGuiHelper::TAMIL_EXTRA_RANGES;
}
}
builder.SetBit(COLOR_INDICATOR_W);
builder.BuildRanges(&uiRanges);
uiRanges.push_back(COLOR_INDICATOR_W);
uiRanges.push_back(COLOR_INDICATOR_W);
uiRanges.push_back(0);
ImFontConfig config;
config.FontDataOwnedByAtlas = false;
@ -469,7 +490,7 @@ void OverlayDrawer::_BuildFontUI(
#endif
_fontUI = fontAtlas.AddFontFromMemoryTTF(
(void*)fontData.data(), (int)fontData.size(), fontSize, &config, uiRanges.Data);
(void*)fontData.data(), (int)fontData.size(), fontSize, &config, uiRanges.data());
if (extraRanges) {
assert(Win32Helper::FileExists(StrHelper::UTF8ToUTF16(extraFontPath).c_str()));

View file

@ -33,7 +33,7 @@ public:
private:
bool _BuildFonts() noexcept;
void _BuildFontUI(std::wstring_view language, const std::vector<uint8_t>& fontData, ImVector<ImWchar>& uiRanges) noexcept;
void _BuildFontUI(std::wstring_view language, const std::vector<uint8_t>& fontData, std::vector<ImWchar>& uiRanges) noexcept;
void _BuildFontFPS(const std::vector<uint8_t>& fontData) noexcept;
struct _EffectDrawInfo {

View file

@ -431,7 +431,7 @@ bool Renderer::_InitFrameSource() noexcept {
}
// 单位为微秒
template<typename Fn>
template <typename Fn>
static int Measure(const Fn& func) noexcept {
using namespace std::chrono;

View file

@ -24,20 +24,20 @@ namespace yas::detail {
// 可平凡复制类型
// 注意不检查指针成员
template<size_t F, typename T>
template <size_t F, typename T>
struct serializer<
type_prop::not_a_fundamental,
ser_case::use_internal_serializer,
F,
T
> {
template<typename Archive, typename = std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, T>>
template <typename Archive, typename = std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, T>>
static Archive& save(Archive& ar, const T& o) noexcept {
ar.write(&o, sizeof(T));
return ar;
}
template<typename Archive, typename = std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, T>>
template <typename Archive, typename = std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, T>>
static Archive& load(Archive& ar, T& o) noexcept {
ar.read(&o, sizeof(T));
return ar;
@ -45,19 +45,19 @@ struct serializer<
};
// SmallVector
template<size_t F, typename T, unsigned N>
template <size_t F, typename T, unsigned N>
struct serializer<
type_prop::not_a_fundamental,
ser_case::use_internal_serializer,
F,
Magpie::SmallVector<T, N>
> {
template<typename Archive>
template <typename Archive>
static Archive& save(Archive& ar, const Magpie::SmallVector<T, N>& vector) noexcept {
return concepts::array::save<F>(ar, vector);
}
template<typename Archive>
template <typename Archive>
static Archive& load(Archive& ar, Magpie::SmallVector<T, N>& vector) noexcept {
return concepts::array::load<F>(ar, vector);
}

View file

@ -359,14 +359,14 @@ protected:
/// Move the range [I, E) into the uninitialized memory starting with "Dest",
/// constructing elements as needed.
template<typename It1, typename It2>
template <typename It1, typename It2>
static void uninitialized_move(It1 I, It1 E, It2 Dest) {
std::uninitialized_move(I, E, Dest);
}
/// Copy the range [I, E) onto the uninitialized memory starting with "Dest",
/// constructing elements as needed.
template<typename It1, typename It2>
template <typename It1, typename It2>
static void uninitialized_copy(It1 I, It1 E, It2 Dest) {
std::uninitialized_copy(I, E, Dest);
}
@ -506,7 +506,7 @@ protected:
/// Move the range [I, E) onto the uninitialized memory
/// starting with "Dest", constructing elements into it as needed.
template<typename It1, typename It2>
template <typename It1, typename It2>
static void uninitialized_move(It1 I, It1 E, It2 Dest) {
// Just do a copy.
uninitialized_copy(I, E, Dest);
@ -514,7 +514,7 @@ protected:
/// Copy the range [I, E) onto the uninitialized memory
/// starting with "Dest", constructing elements into it as needed.
template<typename It1, typename It2>
template <typename It1, typename It2>
static void uninitialized_copy(It1 I, It1 E, It2 Dest) {
// Arbitrary iterator types; just use the basic implementation.
std::uninitialized_copy(I, E, Dest);
@ -1324,13 +1324,13 @@ template <typename Out, typename R> SmallVector<Out> to_vector_of(R&& Range) {
namespace std {
/// Implement std::swap in terms of SmallVector swap.
template<typename T>
template <typename T>
inline void swap(Magpie::SmallVectorImpl<T>& LHS, Magpie::SmallVectorImpl<T>& RHS) {
LHS.swap(RHS);
}
/// Implement std::swap in terms of SmallVector swap.
template<typename T, unsigned N>
template <typename T, unsigned N>
inline void swap(Magpie::SmallVector<T, N>& LHS, Magpie::SmallVector<T, N>& RHS) {
LHS.swap(RHS);
}

View file

@ -14,7 +14,7 @@ struct StrHelper {
static std::string UTF16ToANSI(std::wstring_view str) noexcept;
template<typename CHAR_T>
template <typename CHAR_T>
static void Trim(std::basic_string_view<CHAR_T>& str) noexcept {
for (int i = 0; i < str.size(); ++i) {
if (!isspace(str[i])) {
@ -42,7 +42,7 @@ struct StrHelper {
Trim<wchar_t>(str);
}
template<typename CHAR_T>
template <typename CHAR_T>
static void Trim(std::basic_string<CHAR_T>& str) noexcept {
std::basic_string_view<CHAR_T> sv(str);
Trim(sv);
@ -53,7 +53,7 @@ struct StrHelper {
Trim<char>(str);
}
template<typename CHAR_T>
template <typename CHAR_T>
static std::basic_string<CHAR_T> Trim(const std::basic_string<CHAR_T>& str) noexcept {
std::basic_string<CHAR_T> result = str;
Trim(result);
@ -64,7 +64,7 @@ struct StrHelper {
return Trim<char>(str);
}
template<typename CHAR_T>
template <typename CHAR_T>
static SmallVector<std::basic_string_view<CHAR_T>> Split(std::basic_string_view<CHAR_T> str, CHAR_T delimiter) noexcept {
SmallVector<std::basic_string_view<CHAR_T>> result;
while (!str.empty()) {
@ -136,49 +136,49 @@ struct StrHelper {
return (wchar_t)std::towlower(c);
}
template<typename CHAR_T>
template <typename CHAR_T>
static std::basic_string<CHAR_T> ToUpperCase(std::basic_string_view<CHAR_T> str) noexcept {
std::basic_string<CHAR_T> result(str);
ToUpperCase(result);
return result;
}
template<typename CHAR_T>
template <typename CHAR_T>
static void ToUpperCase(std::basic_string<CHAR_T>& str) noexcept {
for (CHAR_T& c : str) {
c = toupper(c);
}
}
template<typename CHAR_T>
template <typename CHAR_T>
static std::basic_string<CHAR_T> ToLowerCase(std::basic_string_view<CHAR_T> str) noexcept {
std::basic_string<CHAR_T> result(str);
ToLowerCase(result);
return result;
}
template<typename CHAR_T>
template <typename CHAR_T>
static void ToLowerCase(std::basic_string<CHAR_T>& str) noexcept {
for (CHAR_T& c : str) {
c = tolower(c);
}
}
template<typename CHAR_T>
template <typename CHAR_T>
static constexpr size_t StrLen(const CHAR_T* str) noexcept {
// std::char_traits 相比 std::strlen 支持更多字符类型
// 目前 MSVC 使用 __builtin_strlen可以在编译时计算字符串常量的长度
return std::char_traits<CHAR_T>::length(str);
}
template<typename T1, typename T2, typename... AV,
template <typename T1, typename T2, typename... AV,
typename CHAR_T = std::conditional_t<std::is_constructible_v<std::basic_string_view<char>, T1>, char, wchar_t>>
static std::basic_string<CHAR_T> Concat(T1&& s1, T2&& s2, AV&&... args) noexcept {
return _Concat<CHAR_T>(std::forward<T1>(s1), std::forward<T2>(s2), std::forward<AV>(args)...);
}
private:
template<typename CHAR_T>
template <typename CHAR_T>
static std::basic_string<CHAR_T> _Concat(
const std::basic_string_view<CHAR_T>& s1,
const std::basic_string_view<CHAR_T>& s2
@ -189,7 +189,7 @@ private:
return result;
}
template<typename CHAR_T>
template <typename CHAR_T>
static std::basic_string<CHAR_T> _Concat(
const std::basic_string_view<CHAR_T>& s1,
const std::basic_string_view<CHAR_T>& s2,
@ -201,7 +201,7 @@ private:
return result;
}
template<typename CHAR_T>
template <typename CHAR_T>
static std::basic_string<CHAR_T> _Concat(
const std::basic_string_view<CHAR_T>& s1,
const std::basic_string_view<CHAR_T>& s2,
@ -214,7 +214,7 @@ private:
return result;
}
template<typename CHAR_T>
template <typename CHAR_T>
static std::basic_string<CHAR_T> _Concat(
const std::basic_string_view<CHAR_T>& s1,
const std::basic_string_view<CHAR_T>& s2,
@ -228,7 +228,7 @@ private:
return result;
}
template<typename CHAR_T, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename... AV>
template <typename CHAR_T, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename... AV>
static std::basic_string<CHAR_T> _Concat(
T1&& s1,
T2&& s2,
@ -249,7 +249,7 @@ private:
});
}
template<typename CHAR_T>
template <typename CHAR_T>
static std::basic_string<CHAR_T> _ConcatHelper(std::initializer_list<std::basic_string_view<CHAR_T>> args) noexcept {
std::basic_string<CHAR_T> result;

View file

@ -19,6 +19,7 @@ static std::array SUPPORTED_LANGUAGES{
L"id",
L"it",
L"ja",
L"ka",
L"ko",
L"pl",
L"pt-br",

View file

@ -631,6 +631,7 @@
<PRIResource Include="Resources.language-id.resw" />
<PRIResource Include="Resources.language-it.resw" />
<PRIResource Include="Resources.language-ja.resw" />
<PRIResource Include="Resources.language-ka.resw" />
<PRIResource Include="Resources.language-ko.resw" />
<PRIResource Include="Resources.language-pl.resw" />
<PRIResource Include="Resources.language-pt-BR.resw" />

View file

@ -385,6 +385,9 @@
<PRIResource Include="Resources.language-ta.resw">
<Filter>Strings</Filter>
</PRIResource>
<PRIResource Include="Resources.language-ka.resw">
<Filter>Strings</Filter>
</PRIResource>
</ItemGroup>
<ItemGroup>
<Text Include="conanfile.txt" />