MicrosoftDocs / winui-api

Public contributions for the Windows User Interface library
Creative Commons Attribution 4.0 International
79 stars 40 forks source link

Documentation gives incorrect information re. Swapchains. #120

Open Gavin-Williams opened 2 years ago

Gavin-Williams commented 2 years ago

The documentation says ...

Swap chains must run on the main UI thread. This is usually accomplished by calling SetSwapChain on a reference that was initialized as a XAML object element.

That's worded incorrectly. The swap-chain is constructed from the Ui thread. But it probably won't be running on the UI thread. Typically the swap-chain will be running (worked upon) from a another engine thread or even a tertiary graphics thread.

Further, the documentation goes on to discuss the independent input source technique later down the page...

Processing input on background threads Using the xref:Microsoft.UI.Xaml.Controls.SwapChainPanel.CreateCoreIndependentInputSource%2A method, apps can process input and render to a SwapChainPanel entirely on one or more background threads. This enables high performance input and rendering independent of the XAML UI thread.

This is also worded incorrectly. That technique will help with input, because hi-rate input will saturate the thread, but it's not required to enable rendering on a background thread. You should already have graphics commands running on a dedicated thread using something like this...

WorkItemHandler workHandler = new WorkItemHandler((IAsyncAction action) =>
            {
                while (action.Status == AsyncStatus.Started && State == EngineState.Running)
                {
                    Update?.Invoke();
                    if (!Gfx.IsRunning)
                        continue;
                    Render?.Invoke();
                }
                Engine.PreShutdown?.Invoke();
                Engine.Shutdown();
            });
            IAsyncAction mainLoopWorker = ThreadPool.RunAsync(
                workHandler,
                WorkItemPriority.High,
                WorkItemOptions.TimeSliced);

I think that wording should be changed to more correctly describe the likely situation. I would suggest something like the following, combining the discussion into one section. I think maybe even this needs a bit of work and extending, but I think it better describes the issues and solutions.

Swap chains can be bound to the Xaml SwapChainPanel by calling SetSwapChain on the native panel interface of the control. Once bound together, Swap chains can run on the main UI thread, but for performance reasons, and to avoid jitters due to the xaml engine competing for cpu cycles are typically run on a dedicated thread. Also consider that processing input on the same thread that is updating a swap chain can also lead to performance issues, due to the high rate of input events coming in from the mouse. To overcome performance issues due to input events, consider using the xref:Microsoft.UI.Xaml.Controls.SwapChainPanel.CreateCoreIndependentInputSource%2A method, which allows apps to process input events related to the SwapChainPanel entirely on one or more background threads. This enables high performance input and rendering, independent from one another and also independent from the XAML UI thread.

Entangled-Logic commented 2 years ago

Thank you for this post. I have a question. If I run my UI from a new or passed in window, in an included component, and follow this process, will it resolve the same as though I had followed this process from the executable? I'm trying to make my project modular so that variants of UI's, rendering pipelines, shells, algorithms and such can be easily added to the project and made available as options. I'm hoping that framework restrictions do not prevent this.

Gavin-Williams commented 2 years ago

@Hemofelicity I'm not too sure about the situation for multiple Windows. It seems to me like those scenarios are still being worked out. There is a new feature now for supporting multiple windows...

We have stabilized and enabled the creation of multiple windows on the same thread in WinUI 3 applications

So take into account I haven't tried using multiple windows myself, I'll try to answer your worry - I don't think there are any restrictions that would prevent you from modularizing your code. For the single window experience, I open the Xaml window (based upon the basic template) then call into my separate project which fires up it's own threads for updating or rendering , depending on what I want. There's no issue there.

Everything works as expected, and as it should. Because as the docs discuss, there are actually serious performance issues that start impacting your rendering. And it get's to the point that they can't be ignored once you start demanding accurate framerates or smooth animations.

There are new concerns that become apparent once you separate your Xaml, Logic and Rendering. Such as thread safety of functions and classes, dealing with events correctly across threads, taking into account that DirectX can't be given instructions from different threads while it's performing operations (I'm talking about Dx11 here, obviously Dx12 has deferred context and MT capability if you want to do that). But these concerns are architectural concerns rather than deal-breaking performance concerns.

Entangled-Logic commented 2 years ago

@Gavin-Williams Yes. This last paragraph. Because I want a WinUI component that handles UI, and then another component that acts as a shell, executing commands over a rendering pipeline component and other 'stuff', working on the head node of an HPC deployment, where the back end will fall back on classic COM. I've been working with multiple Windows, but I haven't implemented a pipeline yet. I had done all this in classic COM with DirectX10, I just want to leverage a Windows UI. I intend on keeping to Microsoft technologies. I intend to work with DirectX12 on the head node, but the point is to allow for a quick inclusion process of alternative pipelines and UI's. Alternative shells could mean using different scripting languages or C# . .. etc. I will only ever use a single swap chain as I can project onto a single surface one or more viewports. Anyway, thanks again for the post.