Lazily load the settings UI DLL (#15628)

Due to an implementation detail in the Xaml compiler--which wants to
ensure that all metadata providers on an App are available
immediately--we were eagerly loading the settings UI DLL and all of its
dependencies, even in sessions where the user was not going to open
Settings.

By turning off eager provider generation and handling it ourselves, we
get to control exactly when the settings UI is loaded.

This required some gentle poking-through of the barrier between App and
Page, but it is almost certainly worth it.

Turning on the Xaml code generation flag to not generate providers
automatically adds an `AddProvider` member to the internal interface for
the autogenerated XamlMetadataProvider. We needed to switch to using the
internal interface rather than the projected type in our custom App base
class to get at it.

Providers that App/Page use must be initialized by the time we start the
WindowsXamlManager, so we load Control and Controls (ha) eagerly and
early.

It looks like it may save 400ms of CPU time (?) on startup.
This commit is contained in:
Dustin L. Howett 2023-06-28 15:32:51 -05:00 committed by GitHub
parent 72b44888b5
commit 0f41851e67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 46 additions and 5 deletions

View File

@ -9,22 +9,35 @@ namespace winrt::TerminalApp::implementation
IXamlType GetXamlType(const ::winrt::Windows::UI::Xaml::Interop::TypeName& type)
{
return _appProvider.GetXamlType(type);
return AppProvider()->GetXamlType(type);
}
IXamlType GetXamlType(const ::winrt::hstring& fullName)
{
return _appProvider.GetXamlType(fullName);
return AppProvider()->GetXamlType(fullName);
}
::winrt::com_array<::winrt::Windows::UI::Xaml::Markup::XmlnsDefinition> GetXmlnsDefinitions()
{
return _appProvider.GetXmlnsDefinitions();
return AppProvider()->GetXmlnsDefinitions();
}
void AddOtherProvider(const ::winrt::Windows::UI::Xaml::Markup::IXamlMetadataProvider& provider)
{
AppProvider()->AddOtherProvider(provider);
}
private:
bool _contentLoaded{ false };
winrt::TerminalApp::XamlMetaDataProvider _appProvider;
winrt::com_ptr<XamlMetaDataProvider> _appProvider;
winrt::com_ptr<XamlMetaDataProvider> AppProvider()
{
if (!_appProvider)
{
_appProvider = winrt::make_self<XamlMetaDataProvider>();
}
return _appProvider;
}
};
template<typename D, typename... I>

View File

@ -29,6 +29,10 @@ namespace winrt::TerminalApp::implementation
void App::Initialize()
{
// LOAD BEARING
AddOtherProvider(winrt::Microsoft::Terminal::Control::XamlMetaDataProvider{});
AddOtherProvider(winrt::Microsoft::UI::Xaml::XamlTypeInfo::XamlControlsXamlMetaDataProvider{});
const auto dispatcherQueue = winrt::Windows::System::DispatcherQueue::GetForCurrentThread();
if (!dispatcherQueue)
{
@ -102,4 +106,12 @@ namespace winrt::TerminalApp::implementation
// We used to support a pure UWP version of the Terminal. This method
// was only ever used to do UWP-specific setup of our App.
}
void App::PrepareForSettingsUI()
{
if (!std::exchange(_preparedForSettingsUI, true))
{
AddOtherProvider(winrt::Microsoft::Terminal::Settings::Editor::XamlMetaDataProvider{});
}
}
}

View File

@ -19,6 +19,7 @@ namespace winrt::TerminalApp::implementation
TerminalApp::AppLogic Logic();
void Close();
void PrepareForSettingsUI();
bool IsDisposed() const
{
@ -27,8 +28,8 @@ namespace winrt::TerminalApp::implementation
private:
winrt::Windows::UI::Xaml::Hosting::WindowsXamlManager _windowsXamlManager = nullptr;
winrt::Windows::Foundation::Collections::IVector<winrt::Windows::UI::Xaml::Markup::IXamlMetadataProvider> _providers = winrt::single_threaded_vector<Windows::UI::Xaml::Markup::IXamlMetadataProvider>();
bool _bIsClosed = false;
bool _preparedForSettingsUI{ false };
};
}

View File

@ -16,6 +16,11 @@
-->
<DisableEmbeddedXbf>false</DisableEmbeddedXbf>
<XamlComponentResourceLocation>nested</XamlComponentResourceLocation>
<!--
Disable automatic provider generation so that we can control when they initialize.
We do this so we can delay the Settings UI DLL loading until absolutely necessary.
-->
<XamlCodeGenerationControlFlags>DoNotGenerateOtherProviders</XamlCodeGenerationControlFlags>
</PropertyGroup>
<PropertyGroup Label="NuGet Dependencies">
<TerminalCppWinrt>true</TerminalCppWinrt>

View File

@ -18,6 +18,7 @@
#include <til/latch.h>
#include "../../types/inc/utils.hpp"
#include "App.h"
#include "ColorHelper.h"
#include "DebugTapConnection.h"
#include "SettingsTab.h"
@ -3722,6 +3723,15 @@ namespace winrt::TerminalApp::implementation
// If we're holding the settings tab's switch command, don't create a new one, switch to the existing one.
if (!_settingsTab)
{
if (auto app{ winrt::Windows::UI::Xaml::Application::Current().try_as<winrt::TerminalApp::App>() })
{
if (auto appPrivate{ winrt::get_self<implementation::App>(app) })
{
// Lazily load the Settings UI components so that we don't do it on startup.
appPrivate->PrepareForSettingsUI();
}
}
winrt::Microsoft::Terminal::Settings::Editor::MainPage sui{ _settings };
if (_hostingHwnd)
{