2f24a7cf071d384c34b60346c7da259d8ecf6496
[platform/core/csapi/tizenfx.git] / test / Tizen.NUI.Samples / Tizen.NUI.Samples / Samples / DisposeTest.cs
1 using System;
2 using System.Runtime.InteropServices;
3 using Tizen.NUI.BaseComponents;
4 using System.Collections.Generic;
5
6 namespace Tizen.NUI.Samples
7 {
8     // Make custom view to ignore Layout features so we can use Depth information
9     public class Custom3DView : CustomView
10     {
11         // Default View Enabled SizeNegotiations, and It is Breakdown the depth values.
12         // So we need to create CustomView whitch CustomViewBehavior.DisableSizeNegotiation.
13         public Custom3DView() : base("Custom3DView", CustomViewBehaviour.DisableSizeNegotiation)
14         {
15             // Make this object use centered.
16             PositionUsesPivotPoint = true;
17             ParentOrigin = Position.ParentOriginCenter;
18             PivotPoint = Position.PivotPointCenter;
19         }
20
21         public override void OnInitialize()
22         {
23         }
24     }
25
26     // copied from https://github.com/hinohie/nui-demo/blob/geotest/Mesh/Mesh.cs
27     public class DisposeTest : IExample
28     {
29         public struct Vec2
30         {
31             float x;
32             float y;
33             public Vec2(float xIn, float yIn)
34             {
35                 x = xIn;
36                 y = yIn;
37             }
38         }
39         public struct Vec3
40         {
41             float x;
42             float y;
43             float z;
44             public Vec3(float xIn, float yIn, float zIn)
45             {
46                 x = xIn;
47                 y = yIn;
48                 z = zIn;
49             }
50         }
51
52         struct TexturedQuadVertex
53         {
54             public Vec3 position;
55             public Vec3 normal;
56             public Vec2 texcoord;
57         }
58
59         static readonly string VERTEX_SHADER =
60         "attribute mediump vec3 aPosition;\n" +
61         "attribute mediump vec3 aNormal;\n" +
62         "attribute mediump vec2 aTexCoord;\n" +
63         "uniform mediump mat4 uMvpMatrix;\n" +
64         "uniform mediump mat3 uNormalMatrix;\n" +
65         "uniform mediump vec3 uSize;\n" +
66         "varying mediump vec3 vNormal;\n" +
67         "varying mediump vec2 vTexCoord;\n" +
68         "varying mediump vec3 vPosition;\n" +
69         "void main()\n" +
70         "{\n" +
71         "    vec4 pos = vec4(aPosition, 1.0)*vec4(uSize,1.0);\n"+
72         "    gl_Position = uMvpMatrix*pos;\n" +
73         "    vPosition = aPosition;\n" +
74         "    vNormal   = normalize(uNormalMatrix * aNormal);\n" +
75         "    vTexCoord = aTexCoord;\n" +
76         "}\n";
77
78         static readonly string FRAGMENT_SHADER =
79         "uniform lowp vec4 uColor;\n" +
80         "uniform sampler2D sTexture;\n" +
81         "varying mediump vec3 vNormal;\n" +
82         "varying mediump vec2 vTexCoord;\n" +
83         "varying mediump vec3 vPosition;\n" +
84         "mediump vec3 uLightDir = vec3(2.0, 0.5, 1.0);\n" + // constant light dir
85         "mediump vec3 uViewDir  = vec3(0.0, 0.0, 1.0);\n" + // constant view dir.
86         "mediump vec3 uAmbientColor = vec3(0.2, 0.2, 0.2);\n" +
87         "mediump vec3 uDiffuseColor = vec3(0.8, 0.8, 0.8);\n" +
88         "mediump vec3 uSpecularColor = vec3(0.5, 0.5, 0.5);\n" +
89         "void main()\n" +
90         "{\n" +
91         "    mediump vec3 lightdir = normalize(uLightDir);\n" +
92         "    mediump vec3 eyedir   = normalize(uViewDir);\n" +
93         "    mediump vec4 texColor = texture2D( sTexture, vTexCoord ) * uColor;\n" +
94         "    mediump float diffuse = min(max(-dot(vNormal, lightdir) + 0.1, 0.0), 1.0);\n" +
95         "    mediump vec3 reflectdir = reflect(-lightdir, vNormal);\n" +
96         "    mediump float specular = pow(max(0.0, dot(reflectdir, eyedir)), 50.0);\n" +
97         "    mediump vec4 color = texColor * vec4(uAmbientColor + uDiffuseColor * diffuse, 1.0) + vec4(uSpecularColor, 0.0) * specular;\n" +
98         "    gl_FragColor = color;\n" +
99         "}\n";
100
101         // Copy from dali-toolkit/internal/visuals/primitive/primitive-visual.cpp
102         // NOTE. I add one more slices for texture coordinate
103         private global::System.IntPtr SphereVertexDataPtr()
104         {
105             TexturedQuadVertex[] vertices = new TexturedQuadVertex[SPHERE_VERTEX_NUMBER];
106
107             const int slices = SPHERE_SLICES;
108             const int stacks = SPHERE_STACKS;
109             // Build start.
110             {
111                 int vertexIndex = 0; //Track progress through vertices.
112                 float x;
113                 float y;
114                 float z;
115
116                 //Top stack.
117                 vertices[vertexIndex].position = new Vec3(0.0f, 0.5f, 0.0f);
118                 vertices[vertexIndex].normal = new Vec3(0.0f, 1.0f, 0.0f);
119                 vertices[vertexIndex].texcoord = new Vec2(0.5f, 1.0f);
120                 vertexIndex++;
121
122                 //Middle stacks.
123                 for (int i = 1; i < stacks; i++)
124                 {
125                     //Note. This vertex method is not common.
126                     //We set one more vertexes for correct texture coordinate. at j == slices
127                     //j==0 and j==slices have equal position, normal, but there texcoord.x is different
128                     for (int j = 0; j <= slices; j++, vertexIndex++)
129                     {
130                         float cos_j = (float)Math.Cos(2.0f * (float)Math.PI * j / (float)slices);
131                         float sin_j = (float)Math.Sin(2.0f * (float)Math.PI * j / (float)slices);
132                         float cos_i = (float)Math.Cos((float)Math.PI * i / (float)stacks);
133                         float sin_i = (float)Math.Sin((float)Math.PI * i / (float)stacks);
134                         x = cos_j * sin_i;
135                         y = cos_i;
136                         z = sin_j * sin_i;
137
138                         vertices[vertexIndex].position = new Vec3(x / 2.0f, y / 2.0f, z / 2.0f);
139                         vertices[vertexIndex].normal = new Vec3(x, y, z);
140                         vertices[vertexIndex].texcoord = new Vec2((float)j / (float)slices, 1.0f - (float)i / (float)stacks);
141                     }
142                 }
143
144                 //Bottom stack.
145                 vertices[vertexIndex].position = new Vec3(0.0f, -0.5f, 0.0f);
146                 vertices[vertexIndex].normal = new Vec3(0.0f, -1.0f, 0.0f);
147                 vertices[vertexIndex].texcoord = new Vec2(0.5f, 0.0f);
148             }
149             // Build done.
150
151             int length = Marshal.SizeOf(vertices[0]);
152             global::System.IntPtr pA = Marshal.AllocHGlobal(length * SPHERE_VERTEX_NUMBER);
153
154             for (int i = 0; i < SPHERE_VERTEX_NUMBER; i++)
155             {
156                 Marshal.StructureToPtr(vertices[i], pA + i * length, true);
157             }
158
159             return pA;
160         }
161
162         private ushort[] SphereIndexData()
163         {
164             ushort[] indices = new ushort[SPHERE_INDEX_NUMBER];
165             const int slices = SPHERE_SLICES;
166             const int stacks = SPHERE_STACKS;
167
168             // Build start.
169             {
170                 int indiceIndex = 0; //Used to keep track of progress through indices.
171                 int previousCycleBeginning = 1; //Stores the index of the vertex that started the cycle of the previous stack.
172                 int currentCycleBeginning = 1 + slices + 1;
173
174                 //Top stack. Loop from index 1 to index slices, as not counting the very first vertex.
175                 for (int i = 1; i <= slices; i++, indiceIndex += 3)
176                 {
177                     indices[indiceIndex] = 0;
178                     indices[indiceIndex + 1] = (ushort)(i + 1);
179                     indices[indiceIndex + 2] = (ushort)i;
180                 }
181
182                 //Middle Stacks. Want to form triangles between the top and bottom stacks, so loop up to the number of stacks - 2.
183                 //Note. This index method is not common.
184                 //We increase Beginning indexes slices+1 cause we add one more vertexes for correct texture coordinate.
185                 for (int i = 0; i < stacks - 2; i++, previousCycleBeginning += slices + 1, currentCycleBeginning += slices + 1)
186                 {
187                     for (int j = 0; j < slices; j++, indiceIndex += 6)
188                     {
189                         indices[indiceIndex] = (ushort)(previousCycleBeginning + j);
190                         indices[indiceIndex + 1] = (ushort)(previousCycleBeginning + 1 + j);
191                         indices[indiceIndex + 2] = (ushort)(currentCycleBeginning + j);
192                         indices[indiceIndex + 3] = (ushort)(currentCycleBeginning + j);
193                         indices[indiceIndex + 4] = (ushort)(previousCycleBeginning + 1 + j);
194                         indices[indiceIndex + 5] = (ushort)(currentCycleBeginning + 1 + j);
195                     }
196                 }
197
198                 //Bottom stack. Loop around the last stack from the previous loop, and go up to the penultimate vertex.
199                 for (int i = 0; i < slices; i++, indiceIndex += 3)
200                 {
201                     indices[indiceIndex] = (ushort)(previousCycleBeginning + slices + 1);
202                     indices[indiceIndex + 1] = (ushort)(previousCycleBeginning + i);
203                     indices[indiceIndex + 2] = (ushort)(previousCycleBeginning + i + 1);
204                 }
205             }
206             // Build done.
207
208             return indices;
209         }
210         const int SPHERE_SLICES = 30; // >= 3
211         const int SPHERE_STACKS = 20; // >= 1
212         const int SPHERE_VERTEX_NUMBER = (SPHERE_SLICES + 1) * (SPHERE_STACKS - 1) + 2;
213         const int SPHERE_INDEX_NUMBER = 6 * SPHERE_SLICES * (SPHERE_STACKS - 1);
214
215
216         private const int AutoDisposedObjectCount = 10;
217         private const int ManualDisposedObjectCount = 10;
218         private const int RecuvelyDisposedObjectCount = 10;
219         private Window win;
220         private View root;
221         private Timer timer;
222         private bool toggle = false;
223         private string resource;
224         private List<Custom3DView> views;
225         private List<Custom3DView> depthViews; // List of tree-formed views. 0 indexes view is root.
226         private Animation rotateAnimation;
227
228         public void Activate()
229         {
230             win = NUIApplication.GetDefaultWindow();
231             resource = Tizen.Applications.Application.Current.DirectoryInfo.Resource;
232             // Set layer behavior as Layer3D. without this, Rendering will be broken
233             win.GetDefaultLayer().Behavior = Layer.LayerBehavior.Layer3D;
234             root = new View()
235             {
236                 Name = "root",
237                 WidthResizePolicy = ResizePolicyType.FillToParent,
238                 HeightResizePolicy = ResizePolicyType.FillToParent,
239             };
240             win.Add(root);
241
242             views = new List<Custom3DView>();
243             depthViews = new List<Custom3DView>();
244             rotateAnimation = new Animation(1500); //1.5s
245
246             AddManyViews();
247             toggle = true;
248
249             timer = new Timer(3000); //3s
250             timer.Tick += OnTimerTick;
251             timer.Start();
252
253         }
254
255         private bool OnTimerTick(object source, Timer.TickEventArgs e)
256         {
257             toggle = !toggle;
258             if (toggle)
259             {
260                 Tizen.Log.Error("NUI", $"View Creation Start\n");
261                 AddManyViews();
262                 Tizen.Log.Error("NUI", $"View Creation Finish\n");
263             }
264             else
265             {
266                 Tizen.Log.Error("NUI", $"Manual Dispose Start\n");
267                 RemoveAllViews();
268                 Tizen.Log.Error("NUI", $"Manual Dispose Finish\n");
269                 FullGC();
270             }
271             return true;
272         }
273
274         private Geometry GenerateGeometry()
275         {
276             PropertyMap vertexFormat = new PropertyMap();
277             vertexFormat.Add("aPosition", new PropertyValue((int)PropertyType.Vector3));
278             vertexFormat.Add("aNormal", new PropertyValue((int)PropertyType.Vector3));
279             vertexFormat.Add("aTexCoord", new PropertyValue((int)PropertyType.Vector2));
280             PropertyBuffer vertexBuffer = new PropertyBuffer(vertexFormat);
281
282             vertexBuffer.SetData(SphereVertexDataPtr(), SPHERE_VERTEX_NUMBER);
283
284             ushort[] indexBuffer = SphereIndexData();
285
286             Geometry geometry = new Geometry();
287             geometry.AddVertexBuffer(vertexBuffer);
288             geometry.SetIndexBuffer(indexBuffer, SPHERE_INDEX_NUMBER);
289             geometry.SetType(Geometry.Type.TRIANGLES);
290             return geometry;
291         }
292
293         private void AddManyViews()
294         {
295             Random rand = new Random();
296
297             for (int i = 0; i < AutoDisposedObjectCount; i++)
298             {
299                 int viewSize = 150;
300                 var view = new Custom3DView()
301                 {
302                     Size = new Size(viewSize, viewSize, viewSize),
303                     Position = new Position(
304                         rand.Next(10, win.WindowSize.Width - 10) - win.WindowSize.Width / 2,
305                         rand.Next(10, win.WindowSize.Height - 10) - win.WindowSize.Height / 2,
306                         rand.Next(-3 * viewSize, 3 * viewSize)
307                     ),
308                     Name = "Auto_" + i.ToString(),
309                 };
310                 root.Add(view);
311
312                 PixelData pixelData = PixelBuffer.Convert(ImageLoader.LoadImageFromFile(
313                     resource + "/images/PopupTest/circle.jpg",
314                     new Size2D(),
315                     FittingModeType.ScaleToFill
316                 ));
317                 Texture texture = new Texture(
318                     TextureType.TEXTURE_2D,
319                     pixelData.GetPixelFormat(),
320                     pixelData.GetWidth(),
321                     pixelData.GetHeight()
322                 );
323                 texture.Upload(pixelData);
324                 TextureSet textureSet = new TextureSet();
325                 textureSet.SetTexture(0u, texture);
326                 Renderer renderer = new Renderer(GenerateGeometry(), new Shader(VERTEX_SHADER, FRAGMENT_SHADER));
327                 renderer.SetTextures(textureSet);
328                 view.AddRenderer(renderer);
329
330                 rotateAnimation.AnimateBy(view, "Orientation", new Rotation(new Radian(new Degree(360.0f)), Vector3.YAxis));
331             }
332
333             for (int i = 0; i < ManualDisposedObjectCount; i++)
334             {
335                 int viewSize = 150;
336                 var view = new Custom3DView()
337                 {
338                     Size = new Size(viewSize, viewSize, viewSize),
339                     Position = new Position(
340                         rand.Next(10, win.WindowSize.Width - 10) - win.WindowSize.Width / 2,
341                         rand.Next(10, win.WindowSize.Height - 10) - win.WindowSize.Height / 2,
342                         rand.Next(-3 * viewSize, 3 * viewSize)
343                     ),
344                     Name = "Manual_" + i.ToString(),
345                 };
346                 root.Add(view);
347                 views.Add(view);
348
349                 PixelData pixelData = PixelBuffer.Convert(ImageLoader.LoadImageFromFile(
350                     resource + "/images/PaletteTest/red2.jpg",
351                     new Size2D(),
352                     FittingModeType.ScaleToFill
353                 ));
354                 Texture texture = new Texture(
355                     TextureType.TEXTURE_2D,
356                     pixelData.GetPixelFormat(),
357                     pixelData.GetWidth(),
358                     pixelData.GetHeight()
359                 );
360                 texture.Upload(pixelData);
361                 TextureSet textureSet = new TextureSet();
362                 textureSet.SetTexture(0u, texture);
363                 Renderer renderer = new Renderer(GenerateGeometry(), new Shader(VERTEX_SHADER, FRAGMENT_SHADER));
364                 renderer.SetTextures(textureSet);
365                 view.AddRenderer(renderer);
366
367                 rotateAnimation.AnimateBy(view, "Orientation", new Rotation(new Radian(new Degree(-360.0f)), Vector3.YAxis));
368             }
369
370             for (int i = 0; i < RecuvelyDisposedObjectCount; i++)
371             {
372                 int viewSize = 150;
373                 var view = new Custom3DView()
374                 {
375                     // Note that we need to disconnect inherit to make this view shows well.
376                     InheritPosition = false,
377                     InheritOrientation = false,
378
379                     Size = new Size(viewSize, viewSize, viewSize),
380
381                     Position = new Position(
382                         rand.Next(10, win.WindowSize.Width - 10) - win.WindowSize.Width / 2,
383                         rand.Next(10, win.WindowSize.Height - 10) - win.WindowSize.Height / 2,
384                         rand.Next(-3 * viewSize, 3 * viewSize)
385                     ),
386                     Name = "Recursive_" + i.ToString(),
387                 };
388
389                 // Add view recursively.
390                 if(i == 0)
391                 {
392                     root.Add(view);
393                 }
394                 else
395                 {
396                     int parentIndex = rand.Next(i);
397                     var parent = depthViews[parentIndex];
398                     parent.Add(view);
399                 }
400                 depthViews.Add(view);
401
402                 PixelData pixelData = PixelBuffer.Convert(ImageLoader.LoadImageFromFile(
403                     resource + "/images/PaletteTest/rock.jpg",
404                     new Size2D(),
405                     FittingModeType.ScaleToFill
406                 ));
407                 Texture texture = new Texture(
408                     TextureType.TEXTURE_2D,
409                     pixelData.GetPixelFormat(),
410                     pixelData.GetWidth(),
411                     pixelData.GetHeight()
412                 );
413                 texture.Upload(pixelData);
414                 TextureSet textureSet = new TextureSet();
415                 textureSet.SetTexture(0u, texture);
416                 Renderer renderer = new Renderer(GenerateGeometry(), new Shader(VERTEX_SHADER, FRAGMENT_SHADER));
417                 renderer.SetTextures(textureSet);
418                 view.AddRenderer(renderer);
419
420                 //rotateAnimation.AnimateBy(view, "Orientation", new Rotation(new Radian(new Degree(360.0f)), Vector3.ZAxis));
421             }
422             rotateAnimation.Looping = true;
423             rotateAnimation.Play();
424         }
425         private void RemoveAllViews()
426         {
427             uint cnt = root.ChildCount;
428             for (int i = (int)(cnt - 1); i >= 0; i--)
429             {
430                 root.Remove(root.GetChildAt((uint)i));
431             }
432             foreach(var view in views)
433             {
434                 var renderer = view.GetRendererAt(0);
435                 renderer.Dispose();
436                 view.Dispose();
437             }
438             if(depthViews?.Count > 0)
439             {
440                 depthViews[0].DisposeRecursively();
441             }
442
443             views.Clear();
444             depthViews.Clear();
445             rotateAnimation.Clear();
446         }
447
448         private void FullGC()
449         {
450             global::System.GC.Collect();
451             global::System.GC.WaitForPendingFinalizers();
452             global::System.GC.Collect();
453         }
454
455         public void Deactivate()
456         {
457             timer.Stop();
458             RemoveAllViews();
459             rotateAnimation?.Dispose();
460             root.Unparent();
461             root.Dispose();
462
463             // Revert default layer behavior as LayerUI
464             win.GetDefaultLayer().Behavior = Layer.LayerBehavior.LayerUI;
465         }
466     }
467 }