View Issue Details

IDProjectCategoryView StatusLast Update
0001671NoesisGUIUnitypublic2020-05-07 20:09
Reporterrockhound Assigned Tosfernandez  
PrioritynormalSeveritymajor 
Status resolvedResolutionfixed 
Product Version2.2.6 
Target Version3.0.0Fixed in Version3.0.0 
Summary0001671: Adding elements to a panel and removing them is not de-allocating memory
Description

If we check ControlGallery's sample project it seems like memory allocated reported by NoesisSettings in Noesis' Unity Package is going up and down based on which elements are being displayed. Seems like a simple Child = null is enough to trigger the unloading of those resources.

On the other hand in the example provided here, ControlGallery sample itself as well as the Inventory sample are being added to a Grid and later removed but reported memory allocated is not going down.

Steps To Reproduce
  • Download Unity project at: https://we.tl/t-gMO412HYXq
  • Open downloaded Unity project and "Reimport" Assets/Resources/GUI folder to make sure xaml files are imported correctly
  • Keep an inspector open with NoesisSettings selected
  • Press play on scene Assets/Scenes/SampleScene
  • On the open scene:
    -- Pressing on the first 2 buttons removes all elements added to grid and then loads chosen sample and adds to grid
    -- Pressing on the 3rd button simply removes all elements from grid
  • Watch allocated memory grow after repeatedly pressing them.
Attached Files
PlatformAny

Activities

sfernandez

sfernandez

2020-05-01 15:42

manager   ~0006306

Some of the leaks come from event hooking, there is a known problem we described here: https://www.noesisengine.com/docs/3.0/Gui.Core.EventsTutorial.html#weak-events-in-c
Changing ControlsGallery sample code to use a WeakReference as bridge for event hooking makes that sample to properly release memory (you can verify it if you add a GC.Collect + GC.WaitForPendingFinalizers to your ClearAllScreens function).

For the Inventory sample there are other issues I was not able to identify yet, we'll continue working on that.

sfernandez

sfernandez

2020-05-01 15:43

manager   ~0006307

ControlGallery MainWindow.xaml code behind updated.

MainWindow.xaml.cs (8,312 bytes)   
#if UNITY_5_3_OR_NEWER
#define NOESIS
using Noesis;
using UnityEngine;
using Grid = Noesis.Grid;

#else
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Media.Animation;
#endif

namespace ControlGallery
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : UserControl
    {
        public MainWindow()
        {
            System.WeakReference wr = new System.WeakReference(this);
            KeyDown += (s, e) => { ((MainWindow)wr.Target).OnKeyDown(s, e); };
            Initialized += (s, e) => { ((MainWindow)wr.Target).OnInitialized(s, e); };
            InitializeComponent();
        }

        private void OnKeyDown(object sender, KeyEventArgs args)
        {
            NukeIt();
        }

#if NOESIS
        private void InitializeComponent()
        {
            Noesis.GUI.LoadComponent(this, "Assets/Resources/GUI/Samples/ControlGallery/MainWindow.xaml");
        }
#endif

        private void OnInitialized(object sender, EventArgs args)
        {
            System.WeakReference wr = new System.WeakReference(this);
            SizeChanged += (s, e) => { ((MainWindow)wr.Target).OnSizeChanged(s, e); };

            _styleSelector = (ComboBox)FindName("StyleSelector");
            _styleSelector.SelectionChanged += (s, e) => { ((MainWindow)wr.Target).OnStyleSelectionChanged(s, e); };

            _noesisStyleResources = (ResourceDictionary)LoadXaml("NoesisStyle");
            _simpleStyleResources = (ResourceDictionary)LoadXaml("SimpleStyle");
            _windowsStyleResources = (ResourceDictionary)LoadXaml("WindowsStyle");

            _selector = (Grid)FindName("Selector");
            _sampleSelector = (TreeView)FindName("SampleSelector");
            _sampleSelector.SelectedItemChanged += (s, e) => { ((MainWindow)wr.Target).OnSamplesSelectionChanged(s, e); };

            _sampleContainer = (Viewbox)FindName("SampleContainer");
            _sampleContainer1 = (Border)FindName("SampleContainer1");
            _sampleContainer2 = (Border)FindName("SampleContainer2");

            _sampleContainer.Resources = _noesisStyleResources;

            _sampleOverlay = (Grid)FindName("SampleOverlay");
            _sampleOverlay.MouseDown += (s, e) => { ((MainWindow)wr.Target).OnSampleOverlayMouseDown(s, e); };

            _showContainer1 = (Storyboard)Resources["ShowContainer1"];
            _showContainer2 = (Storyboard)Resources["ShowContainer2"];

            _selectorTopContainer = (Border)FindName("SelectorTopContainer");
            _selectorTop = (StackPanel)FindName("SelectorTop");
            _selectorTopExpand = (ToggleButton)FindName("SelectorTopExpand");

            _selectorLeftContainer = (Border)FindName("SelectorLeftContainer");
            _selectorLeft = (StackPanel)FindName("SelectorLeft");
            _selectorLeftExpand = (ToggleButton)FindName("SelectorLeftExpand");

            _itemHeight = (Decorator)FindName("ItemHeight");
        }

        void OnStyleSelectionChanged(object sender, SelectionChangedEventArgs args)
        {
            switch (_styleSelector.SelectedIndex)
            {
                case 0:
                    _sampleContainer.Resources = _noesisStyleResources;
                    break;

                case 1:
                    _sampleContainer.Resources = _simpleStyleResources;
                    break;

                case 2:
                    _sampleContainer.Resources = _windowsStyleResources;
                    break;
            }

            args.Handled = true;
        }

        void OnSamplesSelectionChanged(object sender, RoutedPropertyChangedEventArgs<object> args)
        {
            object newValue = args.NewValue;
            TreeViewItem tvi = (TreeViewItem)newValue;
            if (tvi != null && !tvi.HasItems)
            {
                string sampleName = (string)tvi.Tag;
                if (_lastSample != sampleName)
                {
                    LoadSample(sampleName);
                    _lastSample = sampleName;
                }
            }
        }

        void LoadSample(string sampleName)
        {
            _sampleSelector.IsEnabled = false;

            UIElement sample = (UIElement)LoadXaml(sampleName);

            if (_sampleContainer1.Child == null)
            {
                // Show container 1
                _sampleContainer1.Child = sample;
                _showContainer1.Completed += OnShowSampleCompleted;
                _showContainer1.Begin();
            }
            else
            {
                // Show container 2
                _sampleContainer2.Child = sample;
                _showContainer2.Completed += OnShowSampleCompleted;
                _showContainer2.Begin();
            }
        }

        object LoadXaml(string xaml)
        {
#if NOESIS
            return Noesis.GUI.LoadXaml("Assets/Resources/GUI/Samples/ControlGallery/Data/" + xaml + ".xaml");
#else
            string path = "/Assets/NoesisGUI/Samples/ControlGallery/Data/" + xaml + ".xaml";
            return Application.LoadComponent(new Uri(path, UriKind.Relative));
#endif
        }

        void OnShowSampleCompleted(object sender, EventArgs e)
        {
            if (_sampleContainer1.Visibility == Visibility.Visible)
            {
                // Container 1 shown
                _showContainer1.Completed -= OnShowSampleCompleted;
                _sampleContainer2.Child = null;
            }
            else
            {
                // Container 2 shown
                _showContainer2.Completed -= OnShowSampleCompleted;
                _sampleContainer1.Child = null;
            }

            _sampleSelector.IsEnabled = true;
        }

        void NukeIt()
        {
            _selectorTopContainer.Child = null;
            _selectorLeftContainer.Child = null;
            _sampleContainer1.Child = null;
            _sampleContainer2.Child = null;
            _lastSample = null;
        }

        void OnSizeChanged(object sender, SizeChangedEventArgs e)
        {
            Size newSize = e.NewSize;
            if (newSize.Width > newSize.Height)
            {
                // Landscape
                _selectorTopContainer.Child = null;
                _selectorLeftContainer.Child = _selector;
                _selectorTop.Visibility = Visibility.Collapsed;
                _selectorLeft.Visibility = Visibility.Visible;
                _selectorTopExpand.IsChecked = false;
                _itemHeight.Height = newSize.Width * 0.05f;
            }
            else
            {
                // Portrait
                _selectorLeftContainer.Child = null;
                _selectorTopContainer.Child = _selector;
                _selectorLeft.Visibility = Visibility.Collapsed;
                _selectorTop.Visibility = Visibility.Visible;
                _selectorLeftExpand.IsChecked = false;
                _itemHeight.Height = newSize.Height * 0.05f;
            }
        }

        void OnSampleOverlayMouseDown(object sender, MouseButtonEventArgs e)
        {
            _selectorLeftExpand.IsChecked = false;
            _selectorTopExpand.IsChecked = false;
        }

#region Private members
        ComboBox _styleSelector;
        ResourceDictionary _noesisStyleResources;
        ResourceDictionary _simpleStyleResources;
        ResourceDictionary _windowsStyleResources;

        Grid _selector;
        TreeView _sampleSelector;
        string _lastSample;

        Viewbox _sampleContainer;
        Border _sampleContainer1;
        Border _sampleContainer2;
        Grid _sampleOverlay;

        Storyboard _showContainer1;
        Storyboard _showContainer2;

        Border _selectorTopContainer;
        StackPanel _selectorTop;
        ToggleButton _selectorTopExpand;

        Border _selectorLeftContainer;
        StackPanel _selectorLeft;
        ToggleButton _selectorLeftExpand;

        Decorator _itemHeight;
#endregion
    }
}
MainWindow.xaml.cs (8,312 bytes)   
sfernandez

sfernandez

2020-05-05 00:52

manager   ~0006318

After more investigation I found interactivity classes are also producing memory leaks.
We will fix all the problems related to those classes for the next release.

rockhound

rockhound

2020-05-05 18:35

reporter   ~0006321

Last edited: 2020-05-05 18:39

Thanks sfernandez. Was about to post reporting that replacing event hooks with WeakReferences didn't show any improvements in our project's memory usage. We are looking forward for your next release.

jsantos

jsantos

2020-05-05 21:23

manager   ~0006322

Already fixed in our repository, will be available tomorrow in RC5

Issue History

Date Modified Username Field Change
2020-04-27 20:01 rockhound New Issue
2020-04-27 20:01 rockhound Tag Attached: C#
2020-04-27 20:01 rockhound Tag Attached: Memory
2020-04-27 20:01 rockhound Tag Attached: Unity
2020-04-27 23:55 rockhound Steps to Reproduce Updated
2020-04-28 10:38 sfernandez Assigned To => sfernandez
2020-04-28 10:38 sfernandez Status new => assigned
2020-04-28 10:38 sfernandez Target Version => 3.0.0
2020-04-28 13:39 rockhound Steps to Reproduce Updated
2020-05-01 15:42 sfernandez Status assigned => feedback
2020-05-01 15:42 sfernandez Note Added: 0006306
2020-05-01 15:43 sfernandez File Added: MainWindow.xaml.cs
2020-05-01 15:43 sfernandez Note Added: 0006307
2020-05-05 00:52 sfernandez Note Added: 0006318
2020-05-05 18:35 rockhound Note Added: 0006321
2020-05-05 18:35 rockhound Status feedback => assigned
2020-05-05 18:39 rockhound Note Edited: 0006321
2020-05-05 21:23 jsantos Note Added: 0006322
2020-05-07 20:09 sfernandez Status assigned => resolved
2020-05-07 20:09 sfernandez Resolution open => fixed
2020-05-07 20:09 sfernandez Fixed in Version => 3.0.0
2025-10-10 13:29 jsantos Category Unity3D => Unity