Magpie/src/Magpie.Core/ImGuiFontsCacheManager.cpp
刘旭 6305493bfb
优化游戏内叠加层的性能 (#615)
* feat: 添加 ImGuiBackend

* chore: 优化格式

* refactor: 将 ImGuiBackend 重构为类

* chore: 清理 ImGuiBackend

* chore: 清理 ImGuiBackend

* chore: 清理 ImGuiBackend

* perf: 优化着色器编译

* feat: 添加错误记录

* feat: 不再按需构造字体

* feat: 添加 ImGuiFontCacheManager

* feat: 实现序列化 ImFontAtlas

* feat: 实现反序列话 ImFontAtlas

* feat: 为字体缓存添加版本号

* feat: 在字体缓存中保存调试名称

* feat: 添加禁用字体缓存选项
2023-05-20 20:16:52 +08:00

208 lines
5.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "pch.h"
#include "ImGuiFontsCacheManager.h"
#include <imgui.h>
#include "YasHelper.h"
#include "Logger.h"
#include "Win32Utils.h"
#include "CommonSharedConstants.h"
#include "StrUtils.h"
namespace yas::detail {
// ImVector
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>
static Archive& save(Archive& ar, const ImVector<T>& vector) noexcept {
uint32_t size = (uint32_t)vector.size();
ar& size;
if constexpr (std::integral_constant<bool, can_be_processed_as_byte_array<F, T>::value>::value) {
ar.write(vector.Data, sizeof(T) * vector.Size());
} else {
for (const T& e : vector) {
ar& e;
}
}
return ar;
}
template<typename Archive>
static Archive& load(Archive& ar, ImVector<T>& vector) noexcept {
uint32_t size = 0;
ar& size;
vector.resize(size);
if constexpr (std::integral_constant<bool, can_be_processed_as_byte_array<F, T>::value>::value) {
ar.read(vector.Data, sizeof(T) * vector.Size());
} else {
for (T& e : vector) {
ar& e;
}
}
return ar;
}
};
// 对 ImFontAtlas 的序列化与反序列化来自 https://github.com/ocornut/imgui/issues/6169
template<std::size_t F>
struct serializer<
type_prop::not_a_fundamental,
ser_case::use_internal_serializer,
F,
ImFontAtlas
> {
template<typename Archive>
static Archive& save(Archive& ar, const ImFontAtlas& fontAltas) noexcept {
ar& fontAltas.Flags & fontAltas.TexUvWhitePixel& fontAltas.TexUvLines;
// 为了方便反序列化ImFont 两次分别序列化不同部分
ar& fontAltas.Fonts.size();
for (ImFont* font : fontAltas.Fonts) {
std::string_view name;
if (font->ConfigData && font->ConfigData->Name[0]) {
name = font->ConfigData->Name;
}
ar& name;
ar& font->FontSize;
}
ar& fontAltas.TexWidth& fontAltas.TexHeight;
ar.write(fontAltas.TexPixelsAlpha8, fontAltas.TexWidth * fontAltas.TexHeight);
for (ImFont* font : fontAltas.Fonts) {
ar& font->Glyphs;
}
return ar;
}
template<typename Archive>
static Archive& load(Archive& ar, ImFontAtlas& fontAltas) noexcept {
fontAltas.ClearTexData();
ar& fontAltas.Flags& fontAltas.TexUvWhitePixel& fontAltas.TexUvLines;
int size = 0;
ar& size;
for (int i = 0; i < size; ++i) {
ImFontConfig dummyConfig;
dummyConfig.FontData = IM_ALLOC(1);
dummyConfig.FontDataSize = 1;
dummyConfig.SizePixels = 1.0f;
std::string name;
ar& name;
std::char_traits<char>::copy(dummyConfig.Name, name.data(), name.size() + 1);
ImFont* font = fontAltas.AddFont(&dummyConfig);
font->ConfigData = &fontAltas.ConfigData.back();
font->ConfigDataCount = 1;
font->ContainerAtlas = &fontAltas;
ar& font->FontSize;
}
// TexPixelsAlpha8 应在 AddFont 后AddGlyph前
ar& fontAltas.TexWidth& fontAltas.TexHeight;
int totalPixels = fontAltas.TexWidth * fontAltas.TexHeight;
fontAltas.TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(totalPixels);
ar.read(fontAltas.TexPixelsAlpha8, totalPixels);
for (ImFont* font : fontAltas.Fonts) {
ImVector<ImFontGlyph> glyphs;
ar& glyphs;
for (ImFontGlyph& glyph : glyphs) {
font->AddGlyph(font->ConfigData, glyph.Codepoint, glyph.X0, glyph.Y0, glyph.X1, glyph.Y1,
glyph.U0, glyph.V0, glyph.U1, glyph.V1, glyph.AdvanceX);
font->SetGlyphVisible(glyph.Codepoint, glyph.Visible);
}
font->BuildLookupTable();
}
fontAltas.TexReady = true;
return ar;
}
};
}
namespace Magpie::Core {
// 缓存版本
// 当缓存文件结构有更改时更新它,使旧缓存失效
static constexpr const uint32_t FONTS_CACHE_VERSION = 1;
static std::wstring GetCacheFileName(const std::wstring_view& language) noexcept {
return StrUtils::ConcatW(CommonSharedConstants::CACHE_DIR, L"fonts_", language);
}
void ImGuiFontsCacheManager::Save(std::wstring_view language, const ImFontAtlas& fontAltas) noexcept {
_buffer.reserve(131072);
try {
yas::vector_ostream os(_buffer);
yas::binary_oarchive<yas::vector_ostream<BYTE>, yas::binary> oa(os);
oa& FONTS_CACHE_VERSION& fontAltas;
} catch (...) {
Logger::Get().Error("序列化 ImFontAtlas 失败");
return;
}
if (!Win32Utils::DirExists(CommonSharedConstants::CACHE_DIR)) {
if (!CreateDirectory(CommonSharedConstants::CACHE_DIR, nullptr)) {
Logger::Get().Win32Error("创建 cache 文件夹失败");
return;
}
}
std::wstring cacheFileName = GetCacheFileName(language);
if (!Win32Utils::WriteFile(cacheFileName.c_str(), _buffer.data(), _buffer.size())) {
Logger::Get().Error("保存字体缓存失败");
}
}
bool ImGuiFontsCacheManager::Load(std::wstring_view language, ImFontAtlas& fontAltas) noexcept {
if (_buffer.empty()) {
std::wstring cacheFileName = GetCacheFileName(language);
if (!Win32Utils::FileExists(cacheFileName.c_str())) {
return false;
}
if (!Win32Utils::ReadFile(cacheFileName.c_str(), _buffer) || _buffer.empty()) {
return false;
}
}
try {
yas::mem_istream mi(_buffer.data(), _buffer.size());
yas::binary_iarchive<yas::mem_istream, yas::binary> ia(mi);
uint32_t cacheVersion;
ia& cacheVersion;
if (cacheVersion != FONTS_CACHE_VERSION) {
Logger::Get().Info("字体缓存版本不匹配");
return false;
}
ia& fontAltas;
} catch (...) {
Logger::Get().Error("反序列化失败");
return false;
}
return true;
}
}