[NUI][API10] Fix some memory leak codes what already fixed at API12
authorEunki Hong <eunkiki.hong@samsung.com>
Fri, 19 Jan 2024 04:04:36 +0000 (13:04 +0900)
committerEunki Hong <h.pichulia@gmail.com>
Mon, 18 Mar 2024 08:02:29 +0000 (17:02 +0900)
Let we backport some minor memory leak resolve PR what we already merged.

This PR squash 3 different patches.

---

Add some remarks at BaseHandle class about Dispose

(Follow PR #5893)

Resolve memory leak for DisposeTest sample

There was leak issue since we don't call FreeHGlobal after AllocHGlobal.

And also, let we Dispose Renderer / Geometry / Shader / TextureSet what NUI create.
Those classes memory might give confuse when we test the memory.

(Follow PR #5895)

Call Mashal.FreeHGlobal() what we allocated

Let we free allocated heap memory by Mashal.

It might cause memory leak issue if user try to use VertexBuffer.

And also, let we also fix FrameBrokerBase implementation
that might be the guideline who want to use custom Geometry class.

(Follow PR #5902)

src/Tizen.NUI.Wearable/src/public/Title.cs
src/Tizen.NUI/src/internal/FrameBroker/FrameBrokerBase.cs
src/Tizen.NUI/src/public/BaseComponents/DirectRenderingGLView.cs
src/Tizen.NUI/src/public/BaseComponents/ViewAccessibilityWrappers.cs
src/Tizen.NUI/src/public/Common/BaseHandle.cs
src/Tizen.NUI/src/public/Rendering/VertexBuffer.cs
test/Tizen.NUI.Samples/Tizen.NUI.Samples/Samples/DisposeTest.cs

index 5d3fb1e1d8901b8ee620c4236ae8a2ab80bb37c3..0b53846f26eb1caa18525684efe38b67c332d17c 100755 (executable)
@@ -326,17 +326,24 @@ namespace Tizen.NUI.Components
             vertex3.position = new Vec2(0.5f, -0.5f);
             vertex4.position = new Vec2(0.5f, 0.5f);
 
-
             TexturedQuadVertex[] texturedQuadVertexData = new TexturedQuadVertex[4] { vertex1, vertex2, vertex3, vertex4 };
 
-            int lenght = Marshal.SizeOf(vertex1);
-            IntPtr pA = Marshal.AllocHGlobal(lenght * 4);
+            int length = Marshal.SizeOf(vertex1);
+            IntPtr pA = Marshal.AllocHGlobal(checked(length * 4));
 
-            for (int i = 0; i < 4; i++)
+            try
+            {
+                for (int i = 0; i < 4; i++)
+                {
+                    Marshal.StructureToPtr(texturedQuadVertexData[i], pA + i * length, true);
+                }
+                vertexData.SetData(pA, 4);
+            }
+            finally
             {
-                Marshal.StructureToPtr(texturedQuadVertexData[i], pA + i * lenght, true);
+                // Free AllocHGlobal memory after call PropertyBuffer.SetData()
+                Marshal.FreeHGlobal(pA);
             }
-            vertexData.SetData(pA, 4);
 
             Geometry geometry = new Geometry();
             geometry.AddVertexBuffer(vertexData);
index 1c5afd61640b0a71e98e28967583e3525c5c66e4..ca250f80819bf080e2ca16f663d0a0300787fce1 100755 (executable)
@@ -290,8 +290,15 @@ namespace Tizen.NUI
             public Vec2 position;
         };
 
-        private IntPtr RectangleDataPtr()
+        private PropertyBuffer CreateQuadPropertyBuffer()
         {
+            /* Create Property buffer */
+            PropertyValue value = new PropertyValue((int)PropertyType.Vector2);
+            PropertyMap vertexFormat = new PropertyMap();
+            vertexFormat.Add("aPosition", value);
+
+            PropertyBuffer vertexBuffer = new PropertyBuffer(vertexFormat);
+            
             TexturedQuadVertex vertex1 = new TexturedQuadVertex();
             TexturedQuadVertex vertex2 = new TexturedQuadVertex();
             TexturedQuadVertex vertex3 = new TexturedQuadVertex();
@@ -303,33 +310,38 @@ namespace Tizen.NUI
 
             TexturedQuadVertex[] texturedQuadVertexData = new TexturedQuadVertex[4] { vertex1, vertex2, vertex3, vertex4 };
 
-            int lenght = Marshal.SizeOf(vertex1);
-            IntPtr pA = Marshal.AllocHGlobal(lenght * 4);
+            int length = Marshal.SizeOf(vertex1);
+            IntPtr pA = Marshal.AllocHGlobal(checked(length * 4));
 
-            for (int i = 0; i < 4; i++)
+            try
             {
-                Marshal.StructureToPtr(texturedQuadVertexData[i], pA + i * lenght, true);
+                for (int i = 0; i < 4; i++)
+                {
+                    Marshal.StructureToPtr(texturedQuadVertexData[i], pA + i * length, true);
+                }
+
+                vertexBuffer.SetData(pA, 4);
+            }
+            finally
+            {
+                // Free AllocHGlobal memory after call PropertyBuffer.SetData()
+                Marshal.FreeHGlobal(pA);
             }
 
-            return pA;
+            value.Dispose();
+            vertexFormat.Dispose();
+
+            return vertexBuffer;
         }
 
         private Geometry CreateQuadGeometry()
         {
-            /* Create Property buffer */
-            PropertyValue value = new PropertyValue((int)PropertyType.Vector2);
-            PropertyMap vertexFormat = new PropertyMap();
-            vertexFormat.Add("aPosition", value);
-
-            PropertyBuffer vertexBuffer = new PropertyBuffer(vertexFormat);
-            vertexBuffer.SetData(RectangleDataPtr(), 4);
+            PropertyBuffer vertexBuffer = CreateQuadPropertyBuffer();
 
             Geometry geometry = new Geometry();
             geometry.AddVertexBuffer(vertexBuffer);
             geometry.SetType(Geometry.Type.TRIANGLE_STRIP);
 
-            value.Dispose();
-            vertexFormat.Dispose();
             vertexBuffer.Dispose();
 
             return geometry;
index 79196fa2d485ad20ed3fcb7872b78f865761e1be..c7b6a002b49cf3e1c28fb7d02b850fb7d9f48bdb 100644 (file)
@@ -184,16 +184,26 @@ namespace Tizen.NUI.BaseComponents
             {
                 if (textures != null)
                 {
-                    IntPtr unmanagedPointer = Marshal.AllocHGlobal(sizeof(IntPtr) * textures.Count);
-                    IntPtr[] texturesArray = new IntPtr[textures.Count];
-                    for (int i = 0; i < textures.Count; i++)
+                    int count = textures.Count;
+                    int intptrBytes = checked(Marshal.SizeOf(typeof(IntPtr)) * count);
+                    if (intptrBytes > 0)
                     {
-                        texturesArray[i] = HandleRef.ToIntPtr(Texture.getCPtr(textures[i]));
+                        IntPtr[] texturesArray = new IntPtr[count];
+                        for (int i = 0; i < count; i++)
+                        {
+                            texturesArray[i] = HandleRef.ToIntPtr(Texture.getCPtr(textures[i]));
+                        }
+                        IntPtr unmanagedPointer = Marshal.AllocHGlobal(intptrBytes);
+                        try
+                        {
+                            Marshal.Copy(texturesArray, 0, unmanagedPointer, texturesArray.Length);
+                            Interop.GLView.GlViewBindTextureResources(SwigCPtr, unmanagedPointer, texturesArray.Length);
+                        }
+                        finally
+                        {
+                            Marshal.FreeHGlobal(unmanagedPointer);
+                        }
                     }
-                    System.Runtime.InteropServices.Marshal.Copy(texturesArray, 0, unmanagedPointer, textures.Count);
-
-                    Interop.GLView.GlViewBindTextureResources(SwigCPtr, unmanagedPointer, textures.Count);
-                    Marshal.FreeHGlobal(unmanagedPointer);
                 }
             }
         }
index 6b48d1ce160da35293a4ecb4080afa0b458b8166..817aea5a18d38dda1449d908d8e1080bea86812b 100644 (file)
@@ -45,6 +45,9 @@ namespace Tizen.NUI.BaseComponents
 
             Marshal.StructureToPtr(ad, ptr, false);
             Interop.ControlDevel.DaliAccessibilitySetAccessibilityDelegate(ptr, Convert.ToUInt32(size));
+
+            // Do not free AllocHGlobal memory, for performance. It will be used for native side very frequencly.
+            // Note that Marshal.AllocHGlobal memory will be freed after process terminated.
         }
 
         private static View GetViewFromRefObject(IntPtr refObjectPtr)
index 9399569aec6ecbcde32a1b97807f4b353749570d..d01802ce5be13149e5b6b8ff9f039929ffcc3efb 100755 (executable)
@@ -25,6 +25,11 @@ namespace Tizen.NUI
     /// <summary>
     /// BaseHandle is a handle to an internal Dali resource.
     /// </summary>
+    /// <remarks>
+    /// Internal Dali resources with BaseHandle has reference count internally.<br/>
+    /// And Dali resources will release the object only if reference count become zero.<br/>
+    /// It mean, even we call <see cref="Dispose()"/>, the reousrce will not be released if some native has reference count.
+    /// </remarks>
     /// <since_tizen> 3 </since_tizen>
     public class BaseHandle : Element, global::System.IDisposable
     {
@@ -302,6 +307,10 @@ namespace Tizen.NUI
         /// <summary>
         /// Dispose.
         /// </summary>
+        /// <remarks>
+        /// This method release only C# side objects. If someone hold BaseHandle at Native side<br/>
+        /// the object will not be removed until native side reset the handle.
+        /// </remarks>
         /// <since_tizen> 3 </since_tizen>
         public void Dispose()
         {
index ff52faebb2affc3fb22feddb6072f33b751360c5..e2963a4769099077dc55c2eee29be287b159d845 100755 (executable)
@@ -62,12 +62,21 @@ namespace Tizen.NUI
             int structSize = Marshal.SizeOf<VertexType>();
             global::System.IntPtr buffer = Marshal.AllocHGlobal(structSize * vertices.Length);
 
-            for (int i = 0; i < vertices.Length; i++)
+            try
             {
-                Marshal.StructureToPtr(vertices[i], buffer + i * structSize, true);
+                for (int i = 0; i < vertices.Length; i++)
+                {
+                    Marshal.StructureToPtr(vertices[i], buffer + i * structSize, true);
+                }
+
+                Interop.VertexBuffer.SetData(SwigCPtr, buffer, (uint)vertices.Length);
+            }
+            finally
+            {
+                // Free AllocHGlobal memory after call Interop.VertexBuffer.SetData()
+                Marshal.FreeHGlobal(buffer);
             }
 
-            Interop.VertexBuffer.SetData(SwigCPtr, buffer, (uint)vertices.Length);
             if (NDalicPINVOKE.SWIGPendingException.Pending)
                 throw NDalicPINVOKE.SWIGPendingException.Retrieve();
         }
index a248264d0ee94e7feed9d6c106effbd39329e1c1..7ffd4d8c74e6629eaae9cd10d14badc78696d387 100644 (file)
@@ -92,7 +92,7 @@ namespace Tizen.NUI.Samples
 
         // Copy from dali-toolkit/internal/visuals/primitive/primitive-visual.cpp
         // NOTE. I add one more slices for texture coordinate
-        private global::System.IntPtr SphereVertexDataPtr()
+        private TexturedQuadVertex[] SphereVertexData()
         {
             TexturedQuadVertex[] vertices = new TexturedQuadVertex[SPHERE_VERTEX_NUMBER];
 
@@ -140,15 +140,7 @@ namespace Tizen.NUI.Samples
             }
             // Build done.
 
-            int length = Marshal.SizeOf(vertices[0]);
-            global::System.IntPtr pA = Marshal.AllocHGlobal(length * SPHERE_VERTEX_NUMBER);
-
-            for (int i = 0; i < SPHERE_VERTEX_NUMBER; i++)
-            {
-                Marshal.StructureToPtr(vertices[i], pA + i * length, true);
-            }
-
-            return pA;
+            return vertices;
         }
 
         private ushort[] SphereIndexData()
@@ -204,7 +196,6 @@ namespace Tizen.NUI.Samples
         const int SPHERE_VERTEX_NUMBER = (SPHERE_SLICES + 1) * (SPHERE_STACKS - 1) + 2;
         const int SPHERE_INDEX_NUMBER = 6 * SPHERE_SLICES * (SPHERE_STACKS - 1);
 
-
         private const int AutoDisposedObjectCount = 10;
         private const int ManualDisposedObjectCount = 10;
         private Window win;
@@ -213,8 +204,11 @@ namespace Tizen.NUI.Samples
         private bool toggle = false;
         private string resource;
         private List<Custom3DView> views;
+        private List<Renderer> renderers;
         private Animation rotateAnimation;
 
+        private Dictionary<string, Texture> textureDictionary = new Dictionary<string, Texture>();
+
         public void Activate()
         {
             win = NUIApplication.GetDefaultWindow();
@@ -230,6 +224,7 @@ namespace Tizen.NUI.Samples
             win.Add(root);
 
             views = new List<Custom3DView>();
+            renderers = new List<Renderer>();
             rotateAnimation = new Animation(1500); //1.5s
 
             AddManyViews();
@@ -238,7 +233,6 @@ namespace Tizen.NUI.Samples
             timer = new Timer(3000); //3s
             timer.Tick += OnTimerTick;
             timer.Start();
-
         }
 
         private bool OnTimerTick(object source, Timer.TickEventArgs e)
@@ -258,13 +252,31 @@ namespace Tizen.NUI.Samples
 
         private Geometry GenerateGeometry()
         {
-            PropertyMap vertexFormat = new PropertyMap();
+            using PropertyMap vertexFormat = new PropertyMap();
             vertexFormat.Add("aPosition", new PropertyValue((int)PropertyType.Vector3));
             vertexFormat.Add("aNormal", new PropertyValue((int)PropertyType.Vector3));
             vertexFormat.Add("aTexCoord", new PropertyValue((int)PropertyType.Vector2));
-            PropertyBuffer vertexBuffer = new PropertyBuffer(vertexFormat);
+            using PropertyBuffer vertexBuffer = new PropertyBuffer(vertexFormat);
+
+            TexturedQuadVertex[] vertices = SphereVertexData();
 
-            vertexBuffer.SetData(SphereVertexDataPtr(), SPHERE_VERTEX_NUMBER);
+            int length = Marshal.SizeOf(vertices[0]);
+            global::System.IntPtr pA = Marshal.AllocHGlobal(checked(length * SPHERE_VERTEX_NUMBER));
+
+            try
+            {
+                for (int i = 0; i < SPHERE_VERTEX_NUMBER; i++)
+                {
+                    Marshal.StructureToPtr(vertices[i], pA + i * length, true);
+                }
+
+                vertexBuffer.SetData(pA, SPHERE_VERTEX_NUMBER);
+            }
+            finally
+            {
+                // We can free raw data after SetData call finished.
+                Marshal.FreeHGlobal(pA);
+            }
 
             ushort[] indexBuffer = SphereIndexData();
 
@@ -272,9 +284,51 @@ namespace Tizen.NUI.Samples
             geometry.AddVertexBuffer(vertexBuffer);
             geometry.SetIndexBuffer(indexBuffer, SPHERE_INDEX_NUMBER);
             geometry.SetType(Geometry.Type.TRIANGLES);
+
             return geometry;
         }
 
+        private Shader GenerateShader()
+        {
+            Shader shader = new Shader(VERTEX_SHADER, FRAGMENT_SHADER);
+            return shader;
+        }
+
+        private Renderer GenerateRenderer(string textureUrl)
+        {
+            Texture texture;
+            if (!textureDictionary.TryGetValue(textureUrl, out texture))
+            {
+                // Let we load image only 1 times per each objects
+                using PixelData pixelData = PixelBuffer.Convert(ImageLoader.LoadImageFromFile(
+                    textureUrl,
+                    new Size2D(),
+                    FittingModeType.ScaleToFill
+                ));
+
+                texture = new Texture(
+                    TextureType.TEXTURE_2D,
+                    pixelData.GetPixelFormat(),
+                    pixelData.GetWidth(),
+                    pixelData.GetHeight()
+                );
+                texture.Upload(pixelData);
+                if (!textureDictionary.TryAdd(textureUrl, texture))
+                {
+                    Tizen.Log.Error("NUI", "Something wrong when we try to add Texture into dictionary\n");
+                }
+            }
+            TextureSet textureSet = new TextureSet();
+            textureSet.SetTexture(0u, texture);
+
+            Renderer renderer = new Renderer(GenerateGeometry(), GenerateShader());
+            renderer.SetTextures(textureSet);
+
+            renderers.Add(renderer);
+
+            return renderer;
+        }
+
         private void AddManyViews()
         {
             Random rand = new Random();
@@ -292,24 +346,7 @@ namespace Tizen.NUI.Samples
                     ),
                 };
                 root.Add(view);
-
-                PixelData pixelData = PixelBuffer.Convert(ImageLoader.LoadImageFromFile(
-                    resource + "/images/PopupTest/circle.jpg",
-                    new Size2D(),
-                    FittingModeType.ScaleToFill
-                ));
-                Texture texture = new Texture(
-                    TextureType.TEXTURE_2D,
-                    pixelData.GetPixelFormat(),
-                    pixelData.GetWidth(),
-                    pixelData.GetHeight()
-                );
-                texture.Upload(pixelData);
-                TextureSet textureSet = new TextureSet();
-                textureSet.SetTexture(0u, texture);
-                Renderer renderer = new Renderer(GenerateGeometry(), new Shader(VERTEX_SHADER, FRAGMENT_SHADER));
-                renderer.SetTextures(textureSet);
-                view.AddRenderer(renderer);
+                view.AddRenderer(GenerateRenderer(resource + "/images/PopupTest/circle.jpg"));
 
                 rotateAnimation.AnimateBy(view, "Orientation", new Rotation(new Radian(new Degree(360.0f)), Vector3.YAxis));
             }
@@ -329,23 +366,7 @@ namespace Tizen.NUI.Samples
                 root.Add(view);
                 views.Add(view);
 
-                PixelData pixelData = PixelBuffer.Convert(ImageLoader.LoadImageFromFile(
-                    resource + "/images/PaletteTest/red2.jpg",
-                    new Size2D(),
-                    FittingModeType.ScaleToFill
-                ));
-                Texture texture = new Texture(
-                    TextureType.TEXTURE_2D,
-                    pixelData.GetPixelFormat(),
-                    pixelData.GetWidth(),
-                    pixelData.GetHeight()
-                );
-                texture.Upload(pixelData);
-                TextureSet textureSet = new TextureSet();
-                textureSet.SetTexture(0u, texture);
-                Renderer renderer = new Renderer(GenerateGeometry(), new Shader(VERTEX_SHADER, FRAGMENT_SHADER));
-                renderer.SetTextures(textureSet);
-                view.AddRenderer(renderer);
+                view.AddRenderer(GenerateRenderer(resource + "/images/PaletteTest/red2.jpg"));
 
                 rotateAnimation.AnimateBy(view, "Orientation", new Rotation(new Radian(new Degree(-360.0f)), Vector3.YAxis));
             }
@@ -359,13 +380,21 @@ namespace Tizen.NUI.Samples
             {
                 root.Remove(root.GetChildAt((uint)i));
             }
-            foreach(var view in views)
+            foreach (var view in views)
+            {
+                view?.Dispose();
+            }
+            foreach (var renderer in renderers)
             {
-                var renderer = view.GetRendererAt(0);
-                renderer.Dispose();
-                view.Dispose();
+                renderer?.GetGeometry()?.Dispose();
+                renderer?.GetShader()?.Dispose();
+                renderer?.GetTextures()?.Dispose();
+                renderer?.Dispose();
             }
+
             views.Clear();
+            renderers.Clear();
+
             rotateAnimation.Clear();
         }
 
@@ -383,6 +412,11 @@ namespace Tizen.NUI.Samples
             rotateAnimation?.Dispose();
             root.Unparent();
             root.Dispose();
+
+            textureDictionary.Clear();
+
+            // Revert default layer behavior as LayerUI
+            win.GetDefaultLayer().Behavior = Layer.LayerBehavior.LayerUI;
         }
     }
 }