Let we backport some minor memory leak resolve PR what we already merged.
This PR squash 4 different patches.
---
Fix some incorrect implements when we get Texture relative items
Since now we have a unified pattern to avoid memory leak problem
between NUI and Native side handle count problem,
Let we change some mis-implementated codes relative with Texture.
(Follow PR #5892)
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)
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);
public Texture GetColorTexture()
{
- //to fix memory leak issue, match the handle count with native side.
global::System.IntPtr cPtr = Interop.FrameBuffer.GetColorTexture(SwigCPtr);
- Texture ret = this.GetInstanceSafely<Texture>(cPtr);
- if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+ Texture ret = Registry.GetManagedBaseHandleFromNativePtr(cPtr) as Texture;
+ if (ret != null)
+ {
+ Interop.BaseHandle.DeleteBaseHandle(new global::System.Runtime.InteropServices.HandleRef(this, cPtr));
+ }
+ else
+ {
+ ret = new Texture(cPtr, true);
+ }
+ NDalicPINVOKE.ThrowExceptionIfExists();
return ret;
}
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();
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;
public TransitionItemBase GetTransitionAt(uint index)
{
- //to fix memory leak issue, match the handle count with native side.
IntPtr cPtr = Interop.TransitionSet.GetTransitionAt(SwigCPtr, index);
- HandleRef CPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
- TransitionItemBase ret = Registry.GetManagedBaseHandleFromNativePtr(CPtr.Handle) as TransitionItemBase;
- if (cPtr != IntPtr.Zero && ret == null)
+ TransitionItemBase ret = Registry.GetManagedBaseHandleFromNativePtr(cPtr) as TransitionItemBase;
+ if (ret != null)
{
- ret = new TransitionItemBase(cPtr, false);
- if (NDalicPINVOKE.SWIGPendingException.Pending)
- throw NDalicPINVOKE.SWIGPendingException.Retrieve();
- return ret;
+ Interop.BaseHandle.DeleteBaseHandle(new HandleRef(this, cPtr));
}
- Interop.BaseHandle.DeleteBaseHandle(CPtr);
- CPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
-
- if (NDalicPINVOKE.SWIGPendingException.Pending)
- throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+ else
+ {
+ ret = new TransitionItemBase(cPtr, true);
+ }
+ NDalicPINVOKE.ThrowExceptionIfExists();
return ret;
}
if (textures != null)
{
int count = textures.Count;
- if (count > 0)
+ int intptrBytes = checked(Marshal.SizeOf(typeof(IntPtr)) * count);
+ if (intptrBytes > 0)
{
IntPtr[] texturesArray = new IntPtr[count];
for (int i = 0; i < count; i++)
{
texturesArray[i] = HandleRef.ToIntPtr(Texture.getCPtr(textures[i]));
}
- IntPtr unmanagedPointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)) * count);
- Marshal.Copy(texturesArray, 0, unmanagedPointer, count);
-
- Interop.GLView.GlViewBindTextureResources(SwigCPtr, unmanagedPointer, count);
- Marshal.FreeHGlobal(unmanagedPointer);
+ IntPtr unmanagedPointer = Marshal.AllocHGlobal(intptrBytes);
+ try
+ {
+ Marshal.Copy(texturesArray, 0, unmanagedPointer, texturesArray.Length);
+ Interop.GLView.GlViewBindTextureResources(SwigCPtr, unmanagedPointer, texturesArray.Length);
+ }
+ finally
+ {
+ Marshal.FreeHGlobal(unmanagedPointer);
+ }
}
}
}
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)
/// <since_tizen> 3 </since_tizen>
public Renderer GetRendererAt(uint index)
{
- //to fix memory leak issue, match the handle count with native side.
IntPtr cPtr = Interop.Actor.GetRendererAt(SwigCPtr, index);
- HandleRef CPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
- Renderer ret = Registry.GetManagedBaseHandleFromNativePtr(CPtr.Handle) as Renderer;
- if (cPtr != IntPtr.Zero && ret == null)
+ Renderer ret = Registry.GetManagedBaseHandleFromNativePtr(cPtr) as Renderer;
+ if (ret != null)
{
- ret = new Renderer(cPtr, false);
- if (NDalicPINVOKE.SWIGPendingException.Pending)
- throw NDalicPINVOKE.SWIGPendingException.Retrieve();
- return ret;
+ Interop.BaseHandle.DeleteBaseHandle(new HandleRef(this, cPtr));
}
- Interop.BaseHandle.DeleteBaseHandle(CPtr);
- CPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
-
- if (NDalicPINVOKE.SWIGPendingException.Pending)
- throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+ else
+ {
+ ret = new Renderer(cPtr, true);
+ }
+ NDalicPINVOKE.ThrowExceptionIfExists();
return ret;
}
/// <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
{
/// <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()
{
/// <since_tizen> 3 </since_tizen>
public TextureSet GetTextures()
{
- //to fix memory leak issue, match the handle count with native side.
- System.IntPtr cPtr = Interop.Renderer.GetTextures(SwigCPtr);
- HandleRef CPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
- TextureSet ret = Registry.GetManagedBaseHandleFromNativePtr(CPtr.Handle) as TextureSet;
- if (cPtr != IntPtr.Zero && ret == null)
+ global::System.IntPtr cPtr = Interop.Renderer.GetTextures(SwigCPtr);
+ TextureSet ret = Registry.GetManagedBaseHandleFromNativePtr(cPtr) as TextureSet;
+ if (ret != null)
{
- ret = new TextureSet(cPtr, false);
- if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
- return ret;
+ Interop.BaseHandle.DeleteBaseHandle(new global::System.Runtime.InteropServices.HandleRef(this, cPtr));
}
- Interop.BaseHandle.DeleteBaseHandle(CPtr);
- CPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
-
- if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+ else
+ {
+ ret = new TextureSet(cPtr, true);
+ }
+ NDalicPINVOKE.ThrowExceptionIfExists();
return ret;
}
/// <since_tizen> 3 </since_tizen>
public Texture GetTexture(uint index)
{
- //to fix memory leak issue, match the handle count with native side.
- System.IntPtr cPtr = Interop.TextureSet.GetTexture(SwigCPtr, index);
- HandleRef CPtr = new global::System.Runtime.InteropServices.HandleRef(this, cPtr);
- Texture ret = Registry.GetManagedBaseHandleFromNativePtr(CPtr.Handle) as Texture;
- if (cPtr != System.IntPtr.Zero && ret == null)
+ global::System.IntPtr cPtr = Interop.TextureSet.GetTexture(SwigCPtr, index);
+ Texture ret = Registry.GetManagedBaseHandleFromNativePtr(cPtr) as Texture;
+ if (ret != null)
{
- ret = new Texture(cPtr, false);
- if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
- return ret;
+ Interop.BaseHandle.DeleteBaseHandle(new global::System.Runtime.InteropServices.HandleRef(this, cPtr));
}
- Interop.BaseHandle.DeleteBaseHandle(CPtr);
- CPtr = new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero);
-
- if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+ else
+ {
+ ret = new Texture(cPtr, true);
+ }
+ NDalicPINVOKE.ThrowExceptionIfExists();
return ret;
}
/// <since_tizen> 3 </since_tizen>
public Sampler GetSampler(uint index)
{
- //to fix memory leak issue, match the handle count with native side.
- System.IntPtr cPtr = Interop.TextureSet.GetSampler(SwigCPtr, index);
- Sampler ret = this.GetInstanceSafely<Sampler>(cPtr);
- if (NDalicPINVOKE.SWIGPendingException.Pending) throw NDalicPINVOKE.SWIGPendingException.Retrieve();
+ global::System.IntPtr cPtr = Interop.TextureSet.GetSampler(SwigCPtr, index);
+ Sampler ret = Registry.GetManagedBaseHandleFromNativePtr(cPtr) as Sampler;
+ if (ret != null)
+ {
+ Interop.BaseHandle.DeleteBaseHandle(new global::System.Runtime.InteropServices.HandleRef(this, cPtr));
+ }
+ else
+ {
+ ret = new Sampler(cPtr, true);
+ }
+ NDalicPINVOKE.ThrowExceptionIfExists();
return ret;
}
int structSize = Marshal.SizeOf<VertexType>();
global::System.IntPtr buffer = Marshal.AllocHGlobal(checked(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();
}
// 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];
}
// 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()
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 const int RecuvelyDisposedObjectCount = 10;
private string resource;
private List<Custom3DView> views;
private List<Custom3DView> depthViews; // List of tree-formed views. 0 indexes view is root.
+ private List<Renderer> renderers;
private Animation rotateAnimation;
+ private Dictionary<string, Texture> textureDictionary = new();
+
public void Activate()
{
win = NUIApplication.GetDefaultWindow();
views = new List<Custom3DView>();
depthViews = new List<Custom3DView>();
+ renderers = new List<Renderer>();
rotateAnimation = new Animation(1500); //1.5s
AddManyViews();
timer = new Timer(3000); //3s
timer.Tick += OnTimerTick;
timer.Start();
-
}
private bool OnTimerTick(object source, Timer.TickEventArgs e)
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);
- vertexBuffer.SetData(SphereVertexDataPtr(), SPHERE_VERTEX_NUMBER);
+ TexturedQuadVertex[] vertices = SphereVertexData();
+
+ 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();
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();
Name = "Auto_" + i.ToString(),
};
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));
}
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));
}
}
depthViews.Add(view);
- PixelData pixelData = PixelBuffer.Convert(ImageLoader.LoadImageFromFile(
- resource + "/images/PaletteTest/rock.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/rock.jpg"));
//rotateAnimation.AnimateBy(view, "Orientation", new Rotation(new Radian(new Degree(360.0f)), Vector3.ZAxis));
}
{
root.Remove(root.GetChildAt((uint)i));
}
- foreach(var view in views)
+ foreach (var view in views)
{
- var renderer = view.GetRendererAt(0);
- renderer.Dispose();
- view.Dispose();
+ view?.Dispose();
}
- if(depthViews?.Count > 0)
+ foreach (var renderer in renderers)
{
- depthViews[0].DisposeRecursively();
+ renderer?.GetGeometry()?.Dispose();
+ renderer?.GetShader()?.Dispose();
+ renderer?.GetTextures()?.Dispose();
+ renderer?.Dispose();
+ }
+ if (depthViews?.Count > 0)
+ {
+ depthViews[0]?.DisposeRecursively();
}
views.Clear();
depthViews.Clear();
+ renderers.Clear();
+
rotateAnimation.Clear();
}
root.Unparent();
root.Dispose();
+ textureDictionary.Clear();
+
// Revert default layer behavior as LayerUI
win.GetDefaultLayer().Behavior = Layer.LayerBehavior.LayerUI;
}