microsoft / WindowsAppSDK

The Windows App SDK empowers all Windows desktop apps with modern Windows UI, APIs, and platform features, including back-compat support, shipped via NuGet.
https://docs.microsoft.com/windows/apps/windows-app-sdk/
MIT License
3.81k stars 320 forks source link

WindowsAppSDK 1.6 increases the build time drastically. #4810

Open DierkDroth opened 2 days ago

DierkDroth commented 2 days ago

Describe the bug

With last VS update last week I updated my C# .NET8 solution from WindowsAppSDK 1.5 to WindowsAppSDK 1.6.

Unfortunately this increased the build time drastically: e.g. a project which includes a WindowsAppSDK shared project now takes at least 3 times as long to build as before. Obviously this increased turn around time drastically and slows down the development cycle. I never experienced something like at on any prior WindowsAppSDK update.

How could I go back to the prior building cycle turn arounds?

I also experienced - but this is nothing new though - that building the solution again without something being changed (!) e.g. just by pressing F5 goes through new compiler runs. Why are the building tools (compiler etc.) not aware that nothing was changed?

Please don't ask me for a repo. Demonstrating the issue requires a solution with a substantial number of source files.

Steps to reproduce the bug

Please see above

Expected behavior

No response

Screenshots

No response

NuGet package version

None

Packaging type

No response

Windows version

No response

IDE

No response

Additional context

No response

DarranRowe commented 2 days ago

Just to ask, since it may be obvious for you but not for me. Is this a C++ project or a C# project? I think the "VS update last week" implies C#, but it would be nice to have the language explicitly stated.

DierkDroth commented 2 days ago

Just to ask, since it may be obvious for you but not for me. Is this a C++ project or a C# project? I think the "VS update last week" implies C#, but it would be nice to have the language explicitly stated.

@DarranRowe sorry I forgot to be specific and just amended the case description: this is C# on .NET8

manodasanW commented 2 days ago

At least for the build time change, this might be related to our source generator / analyzer in the Windows SDK projection having a perf impact on larger projects. We do have an updated Windows SDK projection package coming in next month's .NET SDK update with some performance improvements to the generator that I am hoping may help here. If you want to try out the change manually before it is in the .NET SDK, see here for the WindowsSdkPackageVersion to specify.

DierkDroth commented 2 days ago

Thanks for your feedback @manodasanW.

Honestly I have no clue why I would need source generators / analyzers or even any "projection" on building (!) a solution.

Isn't there a way to get rid of all that? If so, what exactly would I need to do?

DierkDroth commented 2 days ago

BTW I have these settings since ever, because I don't need all that stuff

Image

DarranRowe commented 2 days ago

Thanks for your feedback @manodasanW.

Honestly I have no clue why I would need source generators / analyzers or even any "projection" on building (!) a solution.

Isn't there a way to get rid of all that? If so, what exactly would I need to do?

For the projection, the answer is yes, but you honestly don't want to do that.

The Windows App SDK is a set of Windows Runtime components. Windows Runtime components are an evolution of COM. The projection is to make them much easier to use by mapping them to a more natural construct for the language. Without the projection, the components would have to be accessed via the ABI, which is more akin to accessing COM components.

I have had to do that in a C++ project, there was a need to access some Windows App SDK functionality but I was unable to use C++/WinRT (this is the recommended C++ projection). The equivalent of the following:

auto dispqueuec = winrt::Microsoft::UI::Dispatching::DispatcherQueueController::CreateOnCurrentThread();

ended up being:

template <typename Interface, typename RuntimeClass>
struct interface_traits
{
    static_assert(static_assert_helper<Interface>::value, "interface_traits is not specialised for this interface");
};
template <typename Interface, typename RuntimeClass>
struct factory_interface_traits
{
    static_assert(static_assert_helper<Interface>::value, "factory_interface_traits is not specialised for this interface");
};

template <>
struct interface_traits<ABI::Microsoft::UI::Dispatching::IDispatcherQueueController, ABI::Microsoft::UI::Dispatching::DispatcherQueueController>
{
    using type = ABI::Microsoft::UI::Dispatching::IDispatcherQueueController;
    using base_type = ABI::Microsoft::UI::Dispatching::IDispatcherQueueController;
    using class_type = ABI::Microsoft::UI::Dispatching::DispatcherQueueController;
    inline static constexpr std::wstring_view class_name{ init_stringview(RuntimeClass_Microsoft_UI_Dispatching_DispatcherQueueController) };
    inline static constexpr std::wstring_view interface_name{ init_stringview(InterfaceName_Microsoft_UI_Dispatching_IDispatcherQueueController) };
    inline static constexpr bool activatable = false;
};

template <>
struct factory_interface_traits<ABI::Microsoft::UI::Dispatching::IDispatcherQueueControllerStatics, ABI::Microsoft::UI::Dispatching::DispatcherQueueController>
{
    using type = ABI::Microsoft::UI::Dispatching::IDispatcherQueueControllerStatics;
    using class_type = ABI::Microsoft::UI::Dispatching::DispatcherQueueController;
    inline static constexpr std::wstring_view class_name{ init_stringview(RuntimeClass_Microsoft_UI_Dispatching_DispatcherQueueController) };
    inline static constexpr std::wstring_view interface_name{ init_stringview(InterfaceName_Microsoft_UI_Dispatching_IDispatcherQueueControllerStatics) };
};

inline Microsoft::WRL::Wrappers::HString initialise_hstring(const std::wstring_view &runtime_class)
    {
        HRESULT hr = S_OK;
        Microsoft::WRL::Wrappers::HString hs;
        hr = hs.Set(runtime_class.data());

        RoOriginateErrorW(hr, 0, L"Failed to set the runtime class name");
        THROW_IF_FAILED(hr);

        return hs;
    }

template <typename Interface, typename RuntimeClass>
    inline auto get_activation_factory()
    {
        HRESULT hr = S_OK;
        Microsoft::WRL::ComPtr<Interface> ret;
        auto runtime_class = initialise_hstring(factory_interface_traits<Interface, RuntimeClass>::class_name);
        hr = Windows::Foundation::GetActivationFactory(runtime_class, ret.ReleaseAndGetAddressOf());
        if (FAILED(hr))
        {
#if (__cplusplus >= 202002L || (defined _MSVC_LANG && _MSVC_LANG >= 202002L))
            auto l_fmtstr = application::helper::format_to_string(L"Failed to get activation factory. Interface name: {}, RuntimeClass name: {}", factory_interface_traits<Interface, RuntimeClass>::interface_name, factory_interface_traits<Interface, RuntimeClass>::class_name);
#else
            auto l_fmtstr = application::helper::format_to_string(L"Failed to get activation factory. Interface name: %s, RuntimeClass name: %s", factory_interface_traits<Interface, RuntimeClass>::interface_name.data(), factory_interface_traits<Interface, RuntimeClass>::class_name.data());
#endif
            RoOriginateErrorW(hr, static_cast<UINT>(l_fmtstr.length()), l_fmtstr.c_str());
            THROW_IF_FAILED(hr);
        }

        return ret;
    }

...
//Create dispatcher queue function
...

ComPtr<IDispatcherQueueController> disp_queue_ctrl;
auto dqcs = wrl_helpers::get_activation_factory<IDispatcherQueueControllerStatics, DispatcherQueueController>();

THROW_IF_FAILED(dqcs->CreateOnCurrentThread(disp_queue_ctrl.ReleaseAndGetAddressOf()));
...

I could have made it more compact if it was just using the ABI to access the Windows App SDK DispatcherQueue, but I was using some other Windows Runtime components through the ABI so I just added onto what I had.

This highlights two important things. First, using these components through the ABI requires a lot of code, and a lot of it is shared. Secondly, the components are usable for multiple languages.

DierkDroth commented 2 days ago

@DarranRowe thanks for your feedback.

Here is my layman's thinking: What you said is well and fine. Sure, I wanted to have easy-to-use 'wrappers' in my C# environment. But:

Also, I'm sure there must have been something to the same effect with <= WindowsAppSDK 1.5. Why would WindowsAppSDK 1.6 performance be worse in that regard?