UE4
Topic Author
Posts: 62
Joined: 29 Dec 2017, 06:32

Performance about NoesisNotifyArrayPropertyChanged(void*)

18 Apr 2018, 04:29

Hi,
I am making a mini map UI, display some actor's transform info, average 20 actors, update VM info every 0.2s. but meet performance problem
the gap between disable TickIcon and enable TickIcon is about 5 fps on Android. Is there some better method can do this?

// called every 0.2s
void UpdateIcons()
{
	if(MiniMapVM == nullptr)
	{
		return;
	}
	
	MiniVMapM->IconList.Reset();
	//about 10-20 actors
	for(int32 i = 0; ActorList.Num(); ++i)
	{
		IconInfo IconToSet;
		//compute icon location and rotation
		MiniMapVM->IconList.Add(IconToSet);
	}
	
	 if(MiniMapVM->bEnableTick)
		MiniMapVM->NotifyIconListChanged(); // call NoesisNotifyArrayPropertyChanged(&IconList)
}


<UserControl x:Class="GameGUI.MiniMap"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:GameGUI"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">

    <UserControl.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="GameGUIStyle.Xaml"></ResourceDictionary>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </UserControl.Resources>
    

    <Grid>
    	 <!--enable or disable update icons in mini map-->
        <CheckBox x:Name="EnableTickIcon" Content="{Binding Path=Name, RelativeSource={RelativeSource Self}}" 
                          Canvas.Left="1060" Canvas.Top="120" FontSize="24" Foreground="#FF0080FF" Background="#FFF3F3F3" 
                          HorizontalAlignment="Right" VerticalAlignment="Top" IsChecked="{Binding bEnableTickIcon, Mode=OneWayToSource}" Panel.ZIndex="2"/>
        <Label Content="{Binding TitleText}"  Foreground="Red" FontSize="14" RenderTransformOrigin="0.5,0.5" Margin="0,0,200,280" Width="200" Height="20" Panel.ZIndex="1"/>

        <!--
        <Image x:Name="PreComputedMapBackground"  Width="300" Height="300" RenderTransformOrigin="0.5,0.5" Source="{Binding PreComputedMapBackground}" >
        </Image>
	-->
	
	<!--
        <Image x:Name="RealTimeMapBackground" Source="{Binding RealTimeMapBackground}" Visibility="Visible" Width="300" Height="300" RenderTransformOrigin="0.5,0.5">
        </Image>
	-->


        <ItemsControl ItemsSource="{Binding MapIconList}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Image x:Name="Img" Source="{Binding IconTexture}" RenderTransformOrigin="0.5, 0.5"
                               IsHitTestVisible="False" Width="{Binding IconSize.X}" Height="{Binding IconSize.Y}">
                            <Image.RenderTransform>
                                <TransformGroup>
                                    <ScaleTransform/>
                                    <SkewTransform/>
                                    <RotateTransform Angle="{Binding RotateAngle}"/>
                                    <TranslateTransform/>
                                </TransformGroup>
                            </Image.RenderTransform>
                        </Image>

                        <Border x:Name="IconTex" Width="{Binding IconSize.X}" Height="{Binding IconSize.Y}"
                                RenderTransformOrigin="0.5, 0.5" Background="Transparent">
                        
                        </Border>

                        <Popup x:Name="IconTips" IsOpen="{Binding ElementName=IconTex, Path=IsMouseOver, Mode=OneWay}"
                            Placement="MousePoint"  StaysOpen="False">
                            <TextBlock Text="{Binding Tips}" FontFamily="{StaticResource Font_KaiTi}" FontSize="16" Foreground="Blue" Margin="20,0,0,0"></TextBlock>

                        </Popup>
                        
                    </Grid>
                    
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemContainerStyle>
                <Style>
                    <Setter Property="Canvas.Left" Value="{Binding Offset.X}" />
                    <Setter Property="Canvas.Top" Value="{Binding Offset.Y}"/>
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>

    </Grid>
</UserControl>


 
User avatar
hcpizzi
Site Admin
Posts: 212
Joined: 09 Feb 2012, 12:40

Re: Performance about NoesisNotifyArrayPropertyChanged(void*)

23 Apr 2018, 19:25

Hi, sorry about the late reply.

I'm going to look into this, but upfront, I know there's some overhead in the way we deal with arrays, particularly arrays of structs, in Unreal. Each Unreal object has to be wrapped with a Noesis BaseComponent, and in the case of structs, the contents are copied and not referenced by pointers. Also, when an array changes completely from frame to frame, that makes the tree update more expensive as well.

I'll report back once I've done some profiling to figure out exactly where the time is going.
 
User avatar
hcpizzi
Site Admin
Posts: 212
Joined: 09 Feb 2012, 12:40

Re: Performance about NoesisNotifyArrayPropertyChanged(void*)

14 May 2018, 11:59

Hi,

I have been looking into this, and definitely the NotifyArrayPropertyChanged path is the least performing one. I made several attempts, but first I'd like to ask: Why do you create a separate array, instead of using properties in your actors?

The reason I'm asking is because I tried three different methods: Yours using NotifyArrayPropertyChanged, another using NoesisNotifyArrayPropertySet on each member of the array (in my tests the array size was always the same, more on this later), and a third one calling NoesisNotifyPropertyChanged for each property of each member of the array.

The performance improved with each change, but the problem is that the last one requires using an UCLASS instead of USTRUCT, because USTRUCTs wrappers don't implement INotifyPropertyChanged. I was looking into changing this for other reasons, but we've decided against it.

Now, we're looking into why there's a performance difference between the second and third methods, because we feel they should perform equally. The first method basically deletes the whole element tree, and then rebuilds it. So what I'm going to do is to change NotifyArrayPropertyChanged to behave like NoesisNotifyArrayPropertySet for the minimum of the new and old number of elements, and then add or remove elements to account for a possible change in size of the array. That way you can keep calling NotifyArrayPropertyChanged and we take care of everything else inside the function. At the same time we will keep looking to close the performance gap between calling NoesisNotifyArrayPropertySet and calling NoesisNotifyPropertyChanged on each property, so you can keep using USTRUCTs if you desire.

We've also discovered a pathological use case that causes performance to degrade over time in some circumstances, and that will be fixed in the next Noesis SDK release.

I'll keep you posted as these changes happen.
 
User avatar
hcpizzi
Site Admin
Posts: 212
Joined: 09 Feb 2012, 12:40

Re: Performance about NoesisNotifyArrayPropertyChanged(void*)

21 May 2018, 10:57

Hi,

I've made the change I mentioned to NotifyArrayPropertyChanged and the code is on GitHub. We're still working on improving the performance in the SDK, but this should improve things for now and will allow the performance improvements of the SDK to be taken advantage of right away when they are released.

Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 2 guests