perf: 优化 Event 的内存占用和性能

This commit is contained in:
Xu 2024-12-23 19:13:05 +08:00
commit 63df4f9a55
17 changed files with 98 additions and 79 deletions

View file

@ -2,41 +2,45 @@
namespace Magpie {
class EventRevoker {
template <typename T>
class EventRevokerT {
public:
EventRevoker() noexcept = default;
EventRevokerT() noexcept = default;
EventRevoker(const EventRevoker&) noexcept = delete;
EventRevoker(EventRevoker&& other) noexcept = default;
EventRevokerT(const EventRevokerT&) noexcept = delete;
EventRevokerT(EventRevokerT&& other) noexcept {
_Swap(other);
}
EventRevoker& operator=(const EventRevoker& other) noexcept = delete;
EventRevoker& operator=(EventRevoker&& other) noexcept {
EventRevoker(std::move(other)).Swap(*this);
EventRevokerT& operator=(const EventRevokerT&) noexcept = delete;
EventRevokerT& operator=(EventRevokerT&& other) noexcept {
EventRevokerT(std::move(other))._Swap(*this);
return *this;
}
template <typename T>
explicit EventRevoker(T&& revoker) noexcept : _revoker(std::forward<T>(revoker)) {}
EventRevokerT(T* event, T::TokenType token) noexcept : _event(event), _token(token) {}
~EventRevoker() noexcept {
if (_revoker) {
_revoker();
~EventRevokerT() {
if (_event) {
_event->operator()(_token);
}
}
void Swap(EventRevoker& other) noexcept {
std::swap(_revoker, other._revoker);
}
void Revoke() {
if (_revoker) {
_revoker();
_revoker = {};
if (_event) {
_event->operator()(_token);
_event = nullptr;
}
}
private:
std::function<void()> _revoker;
void _Swap(EventRevokerT& other) noexcept {
std::swap(_event, other._event);
std::swap(_token, other._token);
}
T* _event = nullptr;
T::TokenType _token;
};
struct EventToken {
@ -58,6 +62,9 @@ private:
using _FunctionType = std::function<void(TArgs...)>;
public:
using TokenType = EventToken;
using EventRevoker = EventRevokerT<Event>;
#ifdef _DEBUG
~Event() {
_DEBUG_DELEGATE_COUNT -= (int)_delegates.size();
@ -83,13 +90,10 @@ public:
_delegates.erase(it);
}
// 调用者应确保 EventRevoker 在 Event 的生命周期内执行撤销
template <typename T>
EventRevoker operator()(winrt::auto_revoke_t, T&& handler) {
EventToken token = operator()(std::forward<T>(handler));
return EventRevoker([this, token]() {
// 调用者应确保此函数在 Event 的生命周期内执行
operator()(token);
});
return EventRevoker(this, operator()(std::forward<T>(handler)));
}
template <typename... TArgs1>
@ -112,6 +116,8 @@ private:
using _BaseType = Event<TArgs...>;
public:
using EventRevoker = EventRevokerT<MultithreadEvent>;
template <typename T>
EventToken operator()(T&& handler) {
auto lock = _lock.lock_exclusive();
@ -126,7 +132,7 @@ public:
template <typename T>
EventRevoker operator()(winrt::auto_revoke_t, T&& handler) {
auto lock = _lock.lock_exclusive();
return _BaseType::operator()(winrt::auto_revoke, std::forward<T>(handler));
return EventRevoker(this, _BaseType::operator()(std::forward<T>(handler)));
}
template <typename... TArgs1>
@ -142,6 +148,9 @@ private:
// 用于简化 WinRT 组件的创建,和 wil::(un)typed_event 相比支持任意委托类型以及 auto revoke
template <typename T>
struct WinRTEvent {
using TokenType = winrt::event_token;
using EventRevoker = EventRevokerT<WinRTEvent>;
winrt::event_token operator()(const T& handler) {
return _handler.add(handler);
}
@ -152,10 +161,7 @@ struct WinRTEvent {
EventRevoker operator()(winrt::auto_revoke_t, const T& handler) {
winrt::event_token token = operator()(handler);
return EventRevoker([this, token]() {
// 调用者应确保此函数在 Event 的生命周期内执行
operator()(token);
});
return EventRevoker(this, token);
}
template <typename... TArgs>

View file

@ -66,9 +66,9 @@ private:
void _UpdateService_StatusChanged(::Magpie::UpdateStatus status);
void _UpdateService_DownloadProgressChanged(double);
::Magpie::EventRevoker _updateStatusChangedRevoker;
::Magpie::EventRevoker _downloadProgressChangedRevoker;
::Magpie::EventRevoker _showOnHomePageChangedRevoker;
::Magpie::Event<::Magpie::UpdateStatus>::EventRevoker _updateStatusChangedRevoker;
::Magpie::Event<double>::EventRevoker _downloadProgressChangedRevoker;
::Magpie::Event<bool>::EventRevoker _showOnHomePageChangedRevoker;
Imaging::SoftwareBitmapSource _logo{ nullptr };
};

View file

@ -254,6 +254,7 @@ void App::_Uninitialize() {
ShortcutService::Get().Uninitialize();
ToastService::Get().Uninitialize();
_colorValuesChangedRevoker.revoke();
_isShowNotifyIconChangedRevoker.Revoke();
_themeChangedRevoker.Revoke();

View file

@ -73,12 +73,12 @@ private:
CoreDispatcher _dispatcher{ nullptr };
::Magpie::EventRevoker _themeChangedRevoker;
::Magpie::Event<::Magpie::AppTheme>::EventRevoker _themeChangedRevoker;
Windows::UI::ViewManagement::UISettings _uiSettings;
Windows::UI::ViewManagement::UISettings::ColorValuesChanged_revoker _colorValuesChangedRevoker;
std::atomic<bool> _isLightTheme = true;
::Magpie::EventRevoker _isShowNotifyIconChangedRevoker;
::Magpie::Event<bool>::EventRevoker _isShowNotifyIconChangedRevoker;
////////////////////////////////////////////////////
//

View file

@ -100,11 +100,11 @@ private:
void _ScalingService_WndToRestoreChanged(HWND);
::Magpie::EventRevoker _isTimerOnRevoker;
::Magpie::EventRevoker _timerTickRevoker;
::Magpie::EventRevoker _isRunningChangedRevoker;
::Magpie::EventRevoker _wndToRestoreChangedRevoker;
::Magpie::EventRevoker _isShowOnHomePageChangedRevoker;
::Magpie::Event<bool>::EventRevoker _isTimerOnRevoker;
::Magpie::Event<double>::EventRevoker _timerTickRevoker;
::Magpie::Event<bool>::EventRevoker _isRunningChangedRevoker;
::Magpie::Event<HWND>::EventRevoker _wndToRestoreChangedRevoker;
::Magpie::Event<bool>::EventRevoker _isShowOnHomePageChangedRevoker;
bool _showUpdateCard = false;
};

View file

@ -371,7 +371,7 @@ std::pair<POINT, SIZE> MainWindow::_CreateWindow() noexcept {
}
void MainWindow::_UpdateTheme() noexcept {
XamlWindowT::_SetTheme(Content()->ActualTheme() == winrt::ElementTheme::Dark);
XamlWindowT::_SetTheme(App::Get().IsLightTheme());
}
LRESULT MainWindow::_TitleBarWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) noexcept {

View file

@ -27,7 +27,7 @@ private:
void _ResizeTitleBarWindow() noexcept;
EventRevoker _appThemeChangedRevoker;
MultithreadEvent<bool>::EventRevoker _appThemeChangedRevoker;
HWND _hwndTitleBar = NULL;
HWND _hwndMaximizeButton = NULL;

View file

@ -157,8 +157,8 @@ private:
// 可以保存此指针的原因是: 用户停留在此页面时不会有缩放配置被创建或删除
::Magpie::Profile* _data = nullptr;
::Magpie::EventRevoker _appThemeChangedRevoker;
::Magpie::EventRevoker _dpiChangedRevoker;
::Magpie::MultithreadEvent<bool>::EventRevoker _appThemeChangedRevoker;
::Magpie::Event<uint32_t>::EventRevoker _dpiChangedRevoker;
Controls::IconElement _icon{ nullptr };

View file

@ -58,14 +58,14 @@ private:
void _ProfileService_ProfileReordered(uint32_t profileIdx, bool isMoveUp);
::Magpie::EventRevoker _appThemeChangedRevoker;
::Magpie::EventRevoker _dpiChangedRevoker;
::Magpie::MultithreadEvent<bool>::EventRevoker _appThemeChangedRevoker;
::Magpie::Event<uint32_t>::EventRevoker _dpiChangedRevoker;
com_ptr<implementation::NewProfileViewModel> _newProfileViewModel = make_self<implementation::NewProfileViewModel>();
::Magpie::EventRevoker _profileAddedRevoker;
::Magpie::EventRevoker _profileRenamedRevoker;
::Magpie::EventRevoker _profileRemovedRevoker;
::Magpie::EventRevoker _profileMovedRevoker;
::Magpie::Event<::Magpie::Profile&>::EventRevoker _profileAddedRevoker;
::Magpie::Event<uint32_t>::EventRevoker _profileRenamedRevoker;
::Magpie::Event<uint32_t>::EventRevoker _profileRemovedRevoker;
::Magpie::Event<uint32_t, bool>::EventRevoker _profileMovedRevoker;
};
}

View file

@ -100,9 +100,9 @@ private:
uint32_t _movingFromIdx = 0;
::Magpie::EventRevoker _scalingModeAddedRevoker;
::Magpie::EventRevoker _scalingModeMovedRevoker;
::Magpie::EventRevoker _scalingModeRemovedRevoker;
::Magpie::Event<::Magpie::EffectAddedWay>::EventRevoker _scalingModeAddedRevoker;
::Magpie::Event<uint32_t, bool>::EventRevoker _scalingModeMovedRevoker;
::Magpie::Event<uint32_t>::EventRevoker _scalingModeRemovedRevoker;
hstring _renameText;
std::wstring_view _trimedRenameText;

View file

@ -72,9 +72,9 @@ private:
IObservableVector<IInspectable> _scalingModes = single_threaded_observable_vector<IInspectable>();
::Magpie::EventRevoker _scalingModeAddedRevoker;
::Magpie::EventRevoker _scalingModeMovedRevoker;
::Magpie::EventRevoker _scalingModeRemovedRevoker;
::Magpie::Event<::Magpie::EffectAddedWay>::EventRevoker _scalingModeAddedRevoker;
::Magpie::Event<uint32_t, bool>::EventRevoker _scalingModeMovedRevoker;
::Magpie::Event<uint32_t>::EventRevoker _scalingModeRemovedRevoker;
hstring _newScalingModeName;
IVector<IInspectable> _newScalingModeCopyFromList{ nullptr };

View file

@ -75,14 +75,14 @@ private:
ScalingError _CheckSrcWnd(HWND hWnd, bool checkIL) noexcept;
std::unique_ptr<::Magpie::ScalingRuntime> _scalingRuntime;
std::unique_ptr<ScalingRuntime> _scalingRuntime;
winrt::DispatcherTimer _countDownTimer;
// DispatcherTimer 在不显示主窗口时可能停滞,因此使用 ThreadPoolTimer
winrt::Threading::ThreadPoolTimer _checkForegroundTimer{ nullptr };
EventRevoker _isAutoRestoreChangedRevoker;
EventRevoker _shortcutActivatedRevoker;
Event<bool>::EventRevoker _isAutoRestoreChangedRevoker;
Event<winrt::Magpie::ShortcutAction>::EventRevoker _shortcutActivatedRevoker;
std::chrono::steady_clock::time_point _timerStartTimePoint;

View file

@ -33,7 +33,7 @@ private:
ShortcutAction _action = ShortcutAction::COUNT_OR_NONE;
hstring _title;
::Magpie::EventRevoker _shortcutChangedRevoker;
::Magpie::Event<winrt::Magpie::ShortcutAction>::EventRevoker _shortcutChangedRevoker;
::Magpie::Shortcut _shortcut;
Controls::ContentDialog _shortcutDialog{ nullptr };

View file

@ -56,7 +56,7 @@ private:
wil::unique_hwnd _hwndHotkey;
wil::unique_hhook _keyboardHook;
::Magpie::EventRevoker _shortcutChangedRevoker;
Event<winrt::Magpie::ShortcutAction>::EventRevoker _shortcutChangedRevoker;
bool _isKeyboardHookActive = true;
// 用于防止长按时重复触发热键

View file

@ -29,7 +29,7 @@ private:
void _IsLogoShown(bool value);
::Magpie::EventRevoker _appThemeChangedRevoker;
::Magpie::MultithreadEvent<bool>::EventRevoker _appThemeChangedRevoker;
Imaging::SoftwareBitmapSource _logo{ nullptr };
HWND _hwndToast;

View file

@ -133,7 +133,14 @@ void ToastService::_ToastThreadProc() noexcept {
xamlSource.Close();
_dispatcher = nullptr;
// 关闭 DesktopWindowXamlSource 后应清空消息队列
// 关闭 DesktopWindowXamlSource 后应清空消息队列以减少 ToastPage 的引用,但不能
// 防止泄露
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
DispatchMessage(&msg);
}
// 偶尔清空消息队列无用,需要再清空一次,不确定是否 100% 可靠。谢谢你XAML Islands
Sleep(0);
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
DispatchMessage(&msg);
}

View file

@ -83,16 +83,16 @@ protected:
_isMaximized = true;
}
void _SetTheme(bool isDarkTheme) noexcept {
_isDarkTheme = isDarkTheme;
void _SetTheme(bool isLightTheme) noexcept {
_isLightTheme = isLightTheme;
const HWND hWnd = this->Handle();
// Win10 中即使在亮色主题下我们也使用暗色边框,这也是 UWP 窗口的行为
ThemeHelper::SetWindowTheme(
hWnd,
Win32Helper::GetOSVersion().IsWin11() ? isDarkTheme : true,
isDarkTheme
Win32Helper::GetOSVersion().IsWin11() ? !isLightTheme : true,
!isLightTheme
);
if (Win32Helper::GetOSVersion().Is22H2OrNewer()) {
@ -108,8 +108,8 @@ protected:
HBRUSH hbrOld = (HBRUSH)SetClassLongPtr(
hWnd,
GCLP_HBRBACKGROUND,
(INT_PTR)CreateSolidBrush(isDarkTheme ?
ThemeHelper::DARK_TINT_COLOR : ThemeHelper::LIGHT_TINT_COLOR));
(INT_PTR)CreateSolidBrush(isLightTheme ? ThemeHelper::LIGHT_TINT_COLOR : ThemeHelper::DARK_TINT_COLOR)
);
if (hbrOld) {
DeleteObject(hbrOld);
}
@ -242,18 +242,18 @@ protected:
RECT rcRest = ps.rcPaint;
rcRest.top = topBorderHeight;
static bool isDarkBrush = _isDarkTheme;
static HBRUSH backgroundBrush = CreateSolidBrush(isDarkBrush ?
ThemeHelper::DARK_TINT_COLOR : ThemeHelper::LIGHT_TINT_COLOR);
static bool isLightBrush = _isLightTheme;
static HBRUSH backgroundBrush = CreateSolidBrush(isLightBrush ?
ThemeHelper::LIGHT_TINT_COLOR : ThemeHelper::DARK_TINT_COLOR);
if (isDarkBrush != _isDarkTheme) {
isDarkBrush = _isDarkTheme;
if (isLightBrush != _isLightTheme) {
isLightBrush = _isLightTheme;
DeleteBrush(backgroundBrush);
backgroundBrush = CreateSolidBrush(isDarkBrush ?
ThemeHelper::DARK_TINT_COLOR : ThemeHelper::LIGHT_TINT_COLOR);
backgroundBrush = CreateSolidBrush(isLightBrush ?
ThemeHelper::LIGHT_TINT_COLOR : ThemeHelper::DARK_TINT_COLOR);
}
if (isDarkBrush) {
if (!isLightBrush) {
// 这里我们想要黑色背景而不是原始边框
// hack 来自 https://github.com/microsoft/terminal/blob/0ee2c74cd432eda153f3f3e77588164cde95044f/src/cascadia/WindowsTerminal/NonClientIslandWindow.cpp#L1030-L1047
HDC opaqueDc;
@ -388,7 +388,7 @@ protected:
_isMaximized = false;
_isWindowShown = false;
_isDarkTheme = false;
_isLightTheme = true;
_content = nullptr;
@ -401,6 +401,11 @@ protected:
while (PeekMessage(&msg1, nullptr, 0, 0, PM_REMOVE)) {
DispatchMessage(&msg1);
}
// 偶尔清空消息队列无用,需要再清空一次,不确定是否 100% 可靠。谢谢你XAML Islands
Sleep(0);
while (PeekMessage(&msg1, nullptr, 0, 0, PM_REMOVE)) {
DispatchMessage(&msg1);
}
return ret;
}
@ -512,7 +517,7 @@ private:
uint32_t _currentDpi = USER_DEFAULT_SCREEN_DPI;
uint32_t _nativeTopBorderHeight = 1;
bool _isDarkTheme = false;
bool _isLightTheme = true;
bool _isWindowShown = false;
bool _isMaximized = false;
};