suriyun-production / mmorpg-kit-docs

This is document for MMORPG KIT project (https://www.assetstore.unity3d.com/#!/content/110188?aid=1100lGeN)
https://suriyun-production.github.io/mmorpg-kit-docs
48 stars 11 forks source link

Skinned Mesh Renderer Memory leak #2576

Open jacobjgrant opened 1 week ago

jacobjgrant commented 1 week ago

I am experienceing Memory leaks when I use the skinned mesh renderer. When I use the Equipment Model Bones Setup By Human Body Bones Manager. I have also tried the List Replacing Manager and the Bone Names Manager. Which canot find the bone names.

My armour has the correct Armature atached to it and its identical the character rig names, yet it cant find the bones.

image

The renderer works when I use the Equipment Model Bones Setup By Human Body Bones Manager except for the memory leak issue. Is there a way to stop this from happening? Im unsing unity 2022.3.16f1 and the latest version of the kit.

insthync commented 1 week ago

Thank you, for bug reporting, try make changes to EquipmentModelBonesSetupByHumanBodyBonesUpdater.cs

I've forget to dispose

using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
using UnityEngine.Jobs;

namespace MultiplayerARPG
{
    [DefaultExecutionOrder(int.MaxValue - 1)]
    public class EquipmentModelBonesSetupByHumanBodyBonesUpdater : MonoBehaviour
    {
        [System.Serializable]
        public struct PredefinedBone
        {
            public HumanBodyBones boneType;
            public Transform boneTransform;
        }

        public PredefinedBone[] predefinedBones = new PredefinedBone[0];

        private readonly List<Transform> _srcTransforms = new List<Transform>();
        private TransformAccessArray _dstTransforms = new TransformAccessArray();

        private Dictionary<HumanBodyBones, Transform> _predefinedBonesDict;
        public Dictionary<HumanBodyBones, Transform> PredefinedBonesDict
        {
            get
            {
                if (_predefinedBonesDict == null)
                {
                    _predefinedBonesDict = new Dictionary<HumanBodyBones, Transform>();
                    for (int i = 0; i < predefinedBones.Length; ++i)
                    {
                        _predefinedBonesDict.Add(predefinedBones[i].boneType, predefinedBones[i].boneTransform);
                    }
                }
                return _predefinedBonesDict;
            }
        }

        public void PrepareTransforms(Animator src, Animator dst)
        {
#if !UNITY_SERVER
            if (src == null || dst == null)
                return;
            if (_dstTransforms.isCreated)
                _dstTransforms.Dispose();
            List<Transform> tempDstTransforms = new List<Transform>();
            for (int i = 0; i < (int)HumanBodyBones.LastBone; ++i)
            {
                Transform srcTransform = src.GetBoneTransform((HumanBodyBones)i);
                if (srcTransform == null)
                    continue;
                Transform dstTransform = null;
                try
                {
                    dstTransform = dst.GetBoneTransform((HumanBodyBones)i);
                }
                catch (System.Exception)
                {
                    // Some error occuring, skip it.
                }
                if (dstTransform != null)
                {
                    _srcTransforms.Add(srcTransform);
                    tempDstTransforms.Add(dstTransform);
                }
                else if (PredefinedBonesDict.TryGetValue((HumanBodyBones)i, out dstTransform))
                {
                    _srcTransforms.Add(srcTransform);
                    tempDstTransforms.Add(dstTransform);
                }
            }
            _dstTransforms = new TransformAccessArray(tempDstTransforms.ToArray());
#endif
        }

        private void OnDestroy()
        {
#if !UNITY_SERVER
            if (_dstTransforms.isCreated)
                _dstTransforms.Dispose();
#endif
        }

#if !UNITY_SERVER
        private void LateUpdate()
        {
            if (_srcTransforms.Count <= 0 || !_dstTransforms.isCreated)
                return;

            int transformsCount = _srcTransforms.Count;

            // Prepare source data
            NativeArray<Vector3> sourcePositions = new NativeArray<Vector3>(transformsCount, Allocator.TempJob);
            NativeArray<Quaternion> sourceRotations = new NativeArray<Quaternion>(transformsCount, Allocator.TempJob);
            NativeArray<Vector3> sourceLocalScales = new NativeArray<Vector3>(transformsCount, Allocator.TempJob);
            for (int i = 0; i < _srcTransforms.Count; ++i)
            {
                _srcTransforms[i].GetPositionAndRotation(out Vector3 position, out Quaternion rotation);
                sourcePositions[i] = position;
                sourceRotations[i] = rotation;
                sourceLocalScales[i] = _srcTransforms[i].localScale;
            }

            // Prepare jobs
            CopyTransformsJob job = new CopyTransformsJob
            {
                sourcePositions = sourcePositions,
                sourceRotations = sourceRotations,
                sourceLocalScales = sourceLocalScales,
            };

            JobHandle jobHandle = job.Schedule(_dstTransforms);

            jobHandle.Complete();

            sourcePositions.Dispose();
            sourceRotations.Dispose();
            sourceLocalScales.Dispose();
        }
#endif

        [BurstCompile]
        private struct CopyTransformsJob : IJobParallelForTransform
        {
            [ReadOnly]
            public NativeArray<Vector3> sourcePositions;
            [ReadOnly]
            public NativeArray<Quaternion> sourceRotations;
            [ReadOnly]
            public NativeArray<Vector3> sourceLocalScales;

            public void Execute(int index, TransformAccess transform)
            {
                if (!transform.isValid)
                    return;
                transform.SetPositionAndRotation(sourcePositions[index], sourceRotations[index]);
                transform.localScale = sourceLocalScales[index];
            }
        }
    }
}
jacobjgrant commented 1 week ago

That stopeed the leak ty

jacobjgrant commented 1 week ago

Dam. I spoke too soon. Yes the memory leaks have gone, but Its not adding my skinned mesh anymore either. Its just sitting on the ground. Is there any other ideas mate?

image

is the problem the update? Cause I need it to add the mesh.

jacobjgrant commented 1 week ago

I did this and memory leaks have gone...

using System.Collections.Generic; using Unity.Burst; using Unity.Collections; using Unity.Jobs; using UnityEngine; using UnityEngine.Jobs;

namespace MultiplayerARPG { [DefaultExecutionOrder(int.MaxValue - 1)] public class EquipmentModelBonesSetupByHumanBodyBonesUpdater : MonoBehaviour { [System.Serializable] public struct PredefinedBone { public HumanBodyBones boneType; public Transform boneTransform; }

    public PredefinedBone[] predefinedBones = new PredefinedBone[0];

    private readonly List<Transform> _srcTransforms = new List<Transform>();
    private TransformAccessArray _dstTransforms = new TransformAccessArray();

    private Dictionary<HumanBodyBones, Transform> _predefinedBonesDict;
    public Dictionary<HumanBodyBones, Transform> PredefinedBonesDict
    {
        get
        {
            if (_predefinedBonesDict == null)
            {
                _predefinedBonesDict = new Dictionary<HumanBodyBones, Transform>();
                for (int i = 0; i < predefinedBones.Length; ++i)
                {
                    _predefinedBonesDict.Add(predefinedBones[i].boneType, predefinedBones[i].boneTransform);
                }
            }
            return _predefinedBonesDict;
        }
    }

    public void PrepareTransforms(Animator src, Animator dst)
    {

        if (src == null || dst == null)
            return;
        if (_dstTransforms.isCreated)
            _dstTransforms.Dispose();
        List<Transform> tempDstTransforms = new List<Transform>();
        for (int i = 0; i < (int)HumanBodyBones.LastBone; ++i)
        {
            Transform srcTransform = src.GetBoneTransform((HumanBodyBones)i);
            if (srcTransform == null)
                continue;
            Transform dstTransform = null;
            try
            {
                dstTransform = dst.GetBoneTransform((HumanBodyBones)i);
            }
            catch (System.Exception)
            {
                // Some error occuring, skip it.
            }
            if (dstTransform != null)
            {
                _srcTransforms.Add(srcTransform);
                tempDstTransforms.Add(dstTransform);
            }
            else if (PredefinedBonesDict.TryGetValue((HumanBodyBones)i, out dstTransform))
            {
                _srcTransforms.Add(srcTransform);
                tempDstTransforms.Add(dstTransform);
            }
        }
        _dstTransforms = new TransformAccessArray(tempDstTransforms.ToArray());

    }

    private void OnDestroy()
    {

        if (_dstTransforms.isCreated)
            _dstTransforms.Dispose();

    }

    private void LateUpdate()
    {
        if (_srcTransforms.Count <= 0 || !_dstTransforms.isCreated)
            return;

        int transformsCount = _srcTransforms.Count;

        // Prepare source data
        NativeArray<Vector3> sourcePositions = new NativeArray<Vector3>(transformsCount, Allocator.TempJob);
        NativeArray<Quaternion> sourceRotations = new NativeArray<Quaternion>(transformsCount, Allocator.TempJob);
        NativeArray<Vector3> sourceLocalScales = new NativeArray<Vector3>(transformsCount, Allocator.TempJob);
        for (int i = 0; i < _srcTransforms.Count; ++i)
        {
            _srcTransforms[i].GetPositionAndRotation(out Vector3 position, out Quaternion rotation);
            sourcePositions[i] = position;
            sourceRotations[i] = rotation;
            sourceLocalScales[i] = _srcTransforms[i].localScale;
        }

        // Prepare jobs
        CopyTransformsJob job = new CopyTransformsJob
        {
            sourcePositions = sourcePositions,
            sourceRotations = sourceRotations,
            sourceLocalScales = sourceLocalScales,
        };

        JobHandle jobHandle = job.Schedule(_dstTransforms);

        jobHandle.Complete();

        sourcePositions.Dispose();
        sourceRotations.Dispose();
        sourceLocalScales.Dispose();
    }

    [BurstCompile]
    private struct CopyTransformsJob : IJobParallelForTransform
    {
        [ReadOnly]
        public NativeArray<Vector3> sourcePositions;
        [ReadOnly]
        public NativeArray<Quaternion> sourceRotations;
        [ReadOnly]
        public NativeArray<Vector3> sourceLocalScales;

        public void Execute(int index, TransformAccess transform)
        {
            if (!transform.isValid)
                return;
            transform.SetPositionAndRotation(sourcePositions[index], sourceRotations[index]);
            transform.localScale = sourceLocalScales[index];
        }
    }
}

}

jacobjgrant commented 1 week ago

And it adds armour with no leaks... image

insthync commented 1 week ago

You've just delete #if !UNITY_SERVER conditions, I intend to optimize it by not update on server.

I will be better change conditions like this #if !UNITY_SERVER || UNITY_EDITOR

jacobjgrant commented 1 week ago

Ok mate, can you post the updated script once you have optimised it?

insthync commented 1 week ago

I've told you what it should be, it is very easy to make changes by yourself, so I won't post it again.