steveh
Topic Author
Posts: 25
Joined: 07 Oct 2019, 12:50

Offloading Noesis::GUI::LoadXaml to a worker thread

17 Jun 2020, 21:48

Hi guys,

Some of our more complicated windows are taking ~2 seconds to load the XAML files in an optimised build. It seems most of the work is just from parsing the XAML files and parsing large SVG paths. Currently, our UI is loaded and updated on main thread. We are unable to stall main thread for 2 seconds whilst this XAML objects are parsed.

Do you have any suggestions? We could move the loading to a separate worker thread, but then all the DispatcherObjects will be allocated using a separate thread id and we will have to move the UI logic to update the view to this worker thread right?

Ideally I want a "UI Load thread", and then have the ownership transfer to main thread once it's loaded. This is fine if I was just instancing freezable objects, but I don't think there's a way to move the entirety of SlNoesis::LaodXaml to work like this right?

Do you have any suggestions how we could go about doing this without requiring that the view is also updated on the worker thread?

Cheers,

-Steven
Last edited by steveh on 18 Jun 2020, 17:01, edited 1 time in total.
 
User avatar
jsantos
Site Admin
Posts: 2906
Joined: 20 Jan 2012, 17:18
Contact:

Re: Offloading SlNoesis::LoadXaml to a worker thread

18 Jun 2020, 13:00

Hi Steve,

We have clients using an extra thread for the UI: the main thread, the UI thread where all views are updated, and the render thread where interaction with the GPU happens. But in general this tends to over complicate things and we do not recommend it. Problems with load times have been highlighted in the past by more clients (many times this is happening because big resource dictionaries are merged in each XAML instead of using the global app dictionary, you should avoid this), we have the following things in mind:

1. (short term) If you could create a ticket (private if you need it) with a XAML showing the issue, I would like to profile it because I am pretty sure there are things we can optimize.
2. We want to implement something like what you propose, being able to load XAMLs in a worker thread and then transfer the ownership. We can prioritize this task. I assume in this case you will start loading the XAML a few frames before being used? So you will need extra logic to foretell what UI you are going to need, right?
3. (long term) We want to implement binary xaml, a converter that transforms XAMLs to optimized runtime format. We had something like this in Noesis v1.0 and we definitely need something similar in v3.
 
steveh
Topic Author
Posts: 25
Joined: 07 Oct 2019, 12:50

Re: Offloading Noesis::GUI::LoadXaml to a worker thread

18 Jun 2020, 17:01

Hey Jesús, cheers for the response.
Problems with load times have been highlighted in the past by more clients (many times this is happening because big resource dictionaries are merged in each XAML instead of using the global app dictionary, you should avoid this)
Our main theme resource dictionaries are already in the shared global application resources, but we do have a bunch of resource dictionaries which are merged all over the place which contain control templates with a bunch of path geometry on a canvas which we use as an image library. I'll move these out of our user controls up to the application layer which should hopefully improve the load times. Essentially we have our vector images in separate resource dictionaries which are merged with several separate controls:
<UserControl>
	<UserControl.Resources>
		<ResourceDictionary>
			<ResourceDictionary.MergedDictionaries>
				<!-- A list of vector image databases, each resource dictionary just contains a bunch of control templates with SVG path info representing our images -->
				<ResourceDictionary Source="Images/Vector/IconsDatabase.xaml"/>
			</ResourceDictionary.MergedDictionaries>
		</ResourceDictionary>
	</UserControl.Resources>
	<!-- We use a custom control with a controltemplate containing the icon path data. See below for the Icon_A key in a separate resource dictionary -->
	<local:VectorImageControl Template="{StaticResource Icon_A}" />
</UserControl>
And IconsDatabase.xaml just contains a bunch of our icons in a resource database. Each image is a control template which has a canvas wrapped in a viewbox with a bunch of SVG paths:
<ResourceDictionary>
	<ControlTemplate x:Key="Icon_A">
		<Viewbox>
			<Canvas Name="Document" Width="..." Height="..." Clip="...">
				<Canvas Name="Layer_1" Width="..." Height="..." Canvas.Left="..." Canvas.Top="...">
					<Path Data="..." Stretch="Fill" Fill="..." Name="Path" Width="..." Height="..." Canvas.Left="..." Canvas.Top="..." />
				</Canvas>
			</Canvas>
		</ViewBox>
	</ControlTemplate>
</ResourceDictionary>
Each vector image is a different control template. Our pipeline to create these resource dictionaries is Illustrator -> Microsoft Expression -> XAML. Then we have a custom tool I wrote which takes those individual XAML files from Expression and merges them together into a single resource dictionary and allows the artists to give them a unique key and to provide fill colours for the paths. There is a separate resource dictionary for each "category" of images, e.g. we may have "IconsDatabase.xaml", "ShapesDatabase.xaml", "CommonDatabase.xaml", "SectionADatabase.xaml" etc. Right now I have it so the user controls merge the image resource dictionaries that they require, but I may move this up to the application layer as you suggest which should hopefully make things load quicker.
We can prioritize this task. I assume in this case you will start loading the XAML a few frames before being used? So you will need extra logic to foretell what UI you are going to need, right?
That's correct. We have a window which is always loaded which contain things like loading screens. Depending on which section of the game we are entering we load a window and attach it as a child of the parent window, e.g. we have something like the following:
<window>
	<Grid x:Name="CommonUIElements">
		<local:LoadingScreenControl x:Name="LoadingScreen" />
	</Grid>
	<Grid x:Name="ActiveWindows"></Grid>
</window>
Then when we enter say "Section A" of our game, I enable the loading screen control which plays an animation. I then call Noesis::GUI::LoadXaml to instance the SectionAWindow, and then attach it as a child of ActiveWindow:
void UIManager::LoadSectionA()
{
	auto pWindow = DynamicPtrCast<Window>(GUI::LoadXaml(Uri("sectiona.xaml")); // Stalls main thread here for ~2 seconds on PC release
	Grid *pWindowGrid = FindName<Grid>("ActiveWindows");
	UIElementCollection *pGridCollection = nullptr != pWindowGrid ? pWindowGrid->GetChildren() : nullptr;
	if (nullptr != pGridCollection)
	{
		pGridCollection->Clear();
		pGridCollection->Add(pWindow);
	}
}
So if this was to happen on another "UI Load" thread that would be perfectly find since we're basically in a loading screen and we already have an overlay which is over the top of the scene. Once the elements had been loaded and the visual tree had been created, if the ownership could be passed back to main thread that would be a perfect solution for me.
We have clients using an extra thread for the UI: the main thread, the UI thread where all views are updated, and the render thread where interaction with the GPU happens. But in general this tends to over complicate things and we do not recommend it
Yes I agree, I'd like to avoid this if possible. We currently just have main thread and render thread. All UI is loaded and accessed on main thread. If I had to create another thread for the UI then all the UI interaction code in our game logic would also have to be moved to the ui thread which is a large change I'd like to avoid. The normal CPU cost is relatively low, we just see spikes when the XAML files are parsed and the Visual Hierarchy is being built. We're also seeing mini spikes during gameplay as objects become visible and templates are applied. This is particularly noticeable for large ItemControls. I think I can just get around this by forcing the elements to apply their templates sooner rather than when they become visible.
(long term) We want to implement binary xaml, a converter that transforms XAMLs to optimized runtime format. We had something like this in Noesis v1.0 and we definitely need something similar in v3.
This sounds like a good plan.
If you could create a ticket (private if you need it) with a XAML showing the issue, I would like to profile it because I am pretty sure there are things we can optimize.
I think it sounds like the first things I need to do is sort out my resource dictionaries. Once I've done that I'll take another quick look. If it's still causing noticeable stalls I'll try and set up something via a private ticket. Much appreciated.

Cheers,

-Steven
 
User avatar
jsantos
Site Admin
Posts: 2906
Joined: 20 Jan 2012, 17:18
Contact:

Re: Offloading Noesis::GUI::LoadXaml to a worker thread

19 Jun 2020, 12:59

I'll move these out of our user controls up to the application layer which should hopefully improve the load times.
Yes, it will improve load times and also memory usage. Dictionaries are not shared if they are instanced in different XAMLs.
We're also seeing mini spikes during gameplay as objects become visible and templates are applied. This is particularly noticeable for large ItemControls. I think I can just get around this by forcing the elements to apply their templates sooner rather than when they become visible.
Those mini spikes could be related to the tessellator, we improved things a bit in v3.0 but there is still room for improvements. If you could isolate this in a XAML I am sure we can remove or reduce the spikes.
I think it sounds like the first things I need to do is sort out my resource dictionaries. Once I've done that I'll take another quick look. If it's still causing noticeable stalls I'll try and set up something via a private ticket. Much appreciated.
Great, thank you!
 
User avatar
jsantos
Site Admin
Posts: 2906
Joined: 20 Jan 2012, 17:18
Contact:

Re: Offloading Noesis::GUI::LoadXaml to a worker thread

19 Jun 2020, 16:05

#1301 is also related to this
 
nikobarli
Posts: 178
Joined: 26 Apr 2017, 06:23

Re: Offloading Noesis::GUI::LoadXaml to a worker thread

24 Jun 2020, 11:45

We are also having this issue, since our XAML is getting bigger and bigger. It takes ~1s in places just to load the XAML.
I prefer the approach to support optimized binary XAML format so that we can generate the binary XAML on compile time, and cache it on runtime.
 
User avatar
jsantos
Site Admin
Posts: 2906
Joined: 20 Jan 2012, 17:18
Contact:

Re: Offloading Noesis::GUI::LoadXaml to a worker thread

25 Jun 2020, 16:55

We are also having this issue, since our XAML is getting bigger and bigger. It takes ~1s in places just to load the XAML.
I prefer the approach to support optimized binary XAML format so that we can generate the binary XAML on compile time, and cache it on runtime.
Hi Niko, any chance we could profile that XAML?

Yes, binary XAML is coming, but not immediately though. Thanks a lot for your feedback on this.
 
nikobarli
Posts: 178
Joined: 26 Apr 2017, 06:23

Re: Offloading Noesis::GUI::LoadXaml to a worker thread

27 Jun 2020, 18:04

Ok, I will give you more information later.
Please wait for a while because it's been very busy these days ;)

Who is online

Users browsing this forum: No registered users and 1 guest