View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0002360 | NoesisGUI | Unity | public | 2022-06-13 12:08 | 2022-06-17 17:46 |
| Reporter | ttermeer-rcg | Assigned To | sfernandez | ||
| Priority | normal | Severity | major | ||
| Status | resolved | Resolution | no change required | ||
| Product Version | 3.1.4 | ||||
| Target Version | 3.1.5 | Fixed in Version | 3.1.5 | ||
| Summary | 0002360: Texture Memory Leak | ||||
| Description | Using 2D Texture with a converter is never freed. I did not check if this is the only scenario where this happens. It feels it is related to Bindings. It may also comes from loading the texture from files outside the Unity project (this is mandatory for our use case) | ||||
| Steps To Reproduce |
| ||||
| Attached Files | |||||
| Platform | Windows | ||||
|
Hi, looking at the code I see you are creating Texture2D objects dynamically but you are not destroying them. Noesis cannot be in charge of the lifecycle of the texture because we don't own that resource (it could come from an asset handled by Unity). I just made a simple scene without Noesis where I create Texture2D objects and immediately release its C# reference (even forcing a GC collect) and as expected the texture memory is not released. If I take a look at the Memory Profiler and select any of those dynamically created textures it indicates the following: This is a dynamically created Asset Type object, that was either Instantiated, implicitly duplicated or explicitly constructed via 'new UnityEngine.Texture2D()'. It happens the same when I check in your test the textures created in the converter, so Noesis is correctly releasing all its references. You have to manage the lifecycle of the created textures and call Destroy when they are not used anymore. |
|
|
It makes sense however, how can we get a notification it is no longer in use ? There is no accessible callback when the image is no longer used in the Binding/Converter. |
|
|
You can handle the lifecycle of the texture by using a custom Image that creates and destroys the asset when the image is loaded/unloaded. For example you can have the following in the xaml: <local:SessionImage Height="648" Width="1296" SessionId="{Binding TrainingId}"/>And that SessionImage will be just a custom Image element like the attached code. I just test it in your project and it works as expected, freeing texure memory everytime you change the image and when the element is removed from the UI tree. |
|
|
SessionImage.cs (2,204 bytes)
#if UNITY_5_3_OR_NEWER
#define NOESIS
using Noesis;
using UnityEngine;
#else
using System.Windows;
using System.Windows.Threading;
#endif
using System.ComponentModel;
using System.Windows.Input;
using System;
namespace BindingMemoryLeak
{
public class SessionImage : Image
{
public SessionImage()
{
this.Loaded += (s, e) => { ((SessionImage)s)?.LoadAsset(); };
this.Unloaded += (s, e) => { ((SessionImage)s)?.UnloadAsset(); };
}
public int SessionId
{
get { return (int)GetValue(SessionIdProperty); }
set { SetValue(SessionIdProperty, value); }
}
public static readonly DependencyProperty SessionIdProperty = DependencyProperty.Register(
"SessionId", typeof(int), typeof(SessionImage), new PropertyMetadata(0, OnLoadAsset));
public bool UseMipMaps
{
get { return (bool)GetValue(UseMipMapsProperty); }
set { SetValue(UseMipMapsProperty, value); }
}
public static readonly DependencyProperty UseMipMapsProperty = DependencyProperty.Register(
"UseMipMaps", typeof(bool), typeof(SessionImage), new PropertyMetadata(true, OnLoadAsset));
private static void OnLoadAsset(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is SessionImage sessionImage)
{
sessionImage.LoadAsset();
}
}
#if NOESIS
public void LoadAsset()
{
UnloadAsset();
var sessionName = SessionId < 0 ? "SESSION_REST" : $"SESSION_{SessionId}";
Source = LoadImage.GetImageSource("Trainings", $"{sessionName}.png", UseMipMaps, out _currentTexture);
}
public void UnloadAsset()
{
if (_currentTexture != null)
{
UnityEngine.Object.Destroy(_currentTexture);
_currentTexture = null;
}
}
private Texture2D _currentTexture;
#else
public void LoadAsset() { }
public void UnloadAsset() { }
#endif
}
} |
|
|
Thanks this does the trick. It completely slipped my mind to extend the image control. |
|
|
Great, closing this as I think there is nothing wrong in Noesis code about it. |
|
| Date Modified | Username | Field | Change |
|---|---|---|---|
| 2022-06-13 12:08 | ttermeer-rcg | New Issue | |
| 2022-06-13 12:08 | ttermeer-rcg | Tag Attached: Memory | |
| 2022-06-13 12:08 | ttermeer-rcg | Tag Attached: Unity | |
| 2022-06-13 12:08 | ttermeer-rcg | File Added: BindingMemoryleak.zip | |
| 2022-06-13 12:16 | ttermeer-rcg | Description Updated | |
| 2022-06-15 11:12 | sfernandez | Assigned To | => sfernandez |
| 2022-06-15 11:12 | sfernandez | Status | new => assigned |
| 2022-06-15 11:12 | sfernandez | Target Version | => 3.1.5 |
| 2022-06-16 11:01 | sfernandez | Status | assigned => feedback |
| 2022-06-16 11:01 | sfernandez | Note Added: 0007966 | |
| 2022-06-16 11:02 | sfernandez | Note Edited: 0007966 | |
| 2022-06-16 11:02 | sfernandez | Note Edited: 0007966 | |
| 2022-06-16 11:50 | ttermeer-rcg | Note Added: 0007967 | |
| 2022-06-16 11:50 | ttermeer-rcg | Status | feedback => assigned |
| 2022-06-16 12:57 | sfernandez | Status | assigned => feedback |
| 2022-06-16 12:57 | sfernandez | Note Added: 0007968 | |
| 2022-06-16 12:58 | sfernandez | Note Added: 0007969 | |
| 2022-06-16 12:58 | sfernandez | File Added: SessionImage.cs | |
| 2022-06-17 16:27 | ttermeer-rcg | Note Added: 0007972 | |
| 2022-06-17 16:27 | ttermeer-rcg | Status | feedback => assigned |
| 2022-06-17 17:46 | sfernandez | Status | assigned => resolved |
| 2022-06-17 17:46 | sfernandez | Resolution | open => no change required |
| 2022-06-17 17:46 | sfernandez | Fixed in Version | => 3.1.5 |
| 2022-06-17 17:46 | sfernandez | Note Added: 0007975 | |
| 2025-10-10 13:29 | jsantos | Category | Unity3D => Unity |