Sirop4ik
Sirop4ik

Reputation: 5263

How to release memory in Unity?

I see that when I create mesh and textures in Unity on each frame (30fps) feels like Unity doesn't release these data from memory after the usage.

There is my code

private bool UpdateFrame(int frameIdx)
    {
        bool result = true;
        int readyBuffSize = DecoderAPI.stream_get_ready_buffer_size(m_stream);

        if (m_currMeshFrameIndex != frameIdx
            && readyBuffSize > 0)
        {
            m_currMeshFrameIndex = frameIdx;

            IntPtr frame = DecoderAPI.stream_get_next_frame_obj(m_stream);

            if (frame == IntPtr.Zero)
            {
                result = false;
            }
            else
            {
                long sequentialFrameIdx = DecoderAPI.get_sequential_number(frame);
                DebugMethod("UNITY UpdateFrame", $"readyBuffSize :: {readyBuffSize}");
                DebugMethod("UNITY UpdateFrame", $"sequentialFrameIdx :: {sequentialFrameIdx}");

                Mesh releaseFormer = m_meshFilter.mesh;
                m_meshFilter.mesh = CrteateMesh(frame);
                Texture2D texture = CreateTexture(frame);
                m_meshRenderer.material.SetTexture("_MainTex", texture);

                DecoderAPI.stream_release_frame_obj(m_stream, frame);
                Destroy(releaseFormer);  // does not seem to help: even when there are no more allocations in C++ the process grows endlessly
            }
        }

        return result;
    }

private Mesh CrteateMesh(IntPtr frame)
    {
        Mesh mesh = new Mesh();

        //Vertices***
        int vertexCount = DecoderAPI.frame_get_vertex_count(frame);
        byte[] xyzBytes = new byte[vertexCount * 3 * 4];
        IntPtr xyz = DecoderAPI.frame_get_vertex_xyz(frame);
        Vector3[] vertices = new Vector3[vertexCount];

        GCHandle handle = GCHandle.Alloc(vertices, GCHandleType.Pinned);
        IntPtr pointer = handle.AddrOfPinnedObject();
        Marshal.Copy(xyz, xyzBytes, 0, xyzBytes.Length);
        Marshal.Copy(xyzBytes, 0, pointer, xyzBytes.Length);
        handle.Free();

        mesh.vertices = vertices;
        //***

        //Faces***
        int faceCount = DecoderAPI.frame_face_count(frame);
        int trisArrSize = faceCount * 3;
        int[] tris = new int[trisArrSize];
        IntPtr indices = DecoderAPI.frame_face_indices(frame);
        Marshal.Copy(indices, tris, 0, trisArrSize);
        mesh.triangles = tris;
        //***

        mesh.RecalculateNormals();

        //UV***
        int uvCount = DecoderAPI.frame_get_uv_count(frame);
        IntPtr uvData = DecoderAPI.frame_get_uv_data(frame);
        int uvArrSize = uvCount * 2;
        float[] uvArr = new float[uvArrSize];
        Vector2[] uv = new Vector2[uvCount];
        Marshal.Copy(uvData, uvArr, 0, uvArrSize);

        for (int i = 0; i < uvCount; i++)
        {
            Vector2 result = new Vector2(uvArr[i * 2], uvArr[i * 2 + 1]) * new Vector2(1, -1);
            uv[i] = result;
        }

        mesh.uv = uv;
        //***

        if (vertexCount != uvCount)
        {
            long frameId = DecoderAPI.get_sequential_number(frame);
            DebugMethod("UNITY CrteateMesh", $"HERE : in frame id :: {frameId}, vertexCount : {vertexCount}, uvCount : {uvCount}");
        }

        return mesh;
    }

private Texture2D CreateTexture(IntPtr frame)
    {
        IntPtr textureObj = DecoderAPI.frame_get_texture_obj(frame);
        DecoderAPI.TextureInfo textureInfo = DecoderAPI.texture_get_info(textureObj);

        int width = textureInfo.width;
        int height = textureInfo.height;
        int channels = textureInfo.channels;
        int stride = textureInfo.stride;

        //DecoderAPI.ColorType colorType = textureInfo.color_type;

        IntPtr pixels = textureInfo.pixels;
        Texture2D texture = new Texture2D(width, height, TextureFormat.RGB24, false);
        //Texture2D texture = new Texture2D(width, height, TextureFormat.DXT5, false);
        texture.LoadRawTextureData(pixels, width * channels * height);
        texture.Apply();

        return texture;
    }

So, what I do is - I create a mesh and texture for each frame use it and then I expect that Unity should release them from memory after the usage, but no. Ok, I found like this method Destroy(releaseFormer) should help, but anyway it is the same I see in TaskManager that memory grows endlessly...

For test I have tried -> I start my c++ code generate (let's say 100 frames) then I stop it (so my c++ doesn't allocate nothing) and I still see that memory grows up to the end. What I expect is - ok even if Unity doesn't release data that I don't need more, I loaded 100 frames that is it, why memory continues to grow?

Question is - how to release from memory all that frames that I don't need?

EDIT

I have changed this method, added Destroy in proper order

private bool UpdateFrame(int frameIdx)
    {
        bool result = true;
        int readyBuffSize = -1;

        if (m_stream != IntPtr.Zero)
        {
            readyBuffSize = DecoderAPI.stream_get_ready_buffer_size(m_stream);
        }

        if (m_currMeshFrameIndex != frameIdx
            && readyBuffSize > 0)
        {
            m_currMeshFrameIndex = frameIdx;

            IntPtr frame = DecoderAPI.stream_get_next_frame_obj(m_stream);

            if (frame == IntPtr.Zero)
            {
                result = false;
            }
            else
            {
                long sequentialFrameIdx = DecoderAPI.frame_get_sequential_number(frame);
                DebugMethod("UNITY UpdateFrame", $"readyBuffSize :: {readyBuffSize}");
                DebugMethod("UNITY UpdateFrame", $"sequentialFrameIdx :: {sequentialFrameIdx}");

                if (m_meshFilter.mesh != null)
                {
                    Destroy(m_meshFilter.mesh);
                }

                m_meshFilter.mesh = CrteateMesh(frame);

                if (m_texture != null)
                {
                    Destroy(m_texture);
                }

                m_texture = CreateTexture(frame);
                m_meshRenderer.material.SetTexture("_MainTex", m_texture);

                if (m_stream != IntPtr.Zero)
                {
                    DecoderAPI.stream_release_frame_obj(m_stream, frame);
                }
            }
        }

        return result;
    }

Upvotes: 0

Views: 3647

Answers (1)

Ben Peterson
Ben Peterson

Reputation: 106

releaseFormer is the mesh right? Did you try calling Destroy on the texture object itself?

Another thread suggested Resources.UnloadUnusedAssets()

Personally I'd be trying to do this with a RenderTexture especially if the texture size doesn't change too often, though might not be possible for your use case

Upvotes: 2

Related Questions