使用AssetBundle中的模型时的内存记忆泄漏及其解决方法

最終更新: 2020年10月21日

在从AssetBundle读取和卸载使用剪贴的模型的Prefab时,可能会发生内存记忆泄漏,具体取决于CubismMaskTexture的处理方式。

这里将说明其原因、未应对的原因以及应对措施。

概述

如果您想在Unity中处理设置了剪贴的模型,请使用CubismMaskController。
您在CubismMaskController.MaskTexture中设置的蒙版纹理(CubismMaskTexture副本),如果未设置,则将在原始化时参考应用程序内的GlobalMaskTexture。
但是,根据项目的规格,CubismMaskTexture的副本可能包含在AssetBundle中,而不是应用程序端。
在这种情况下,从AssetBundle读取和卸载模型可能会导致内存记忆泄漏。

造成内存记忆泄漏的原因是CubismMaskTexture内部的RenderTexture生成并保留。
根据CubismMaskTexture的使用方式,有两种对策。

case 1: 如果所有模型都使用一种CubismMaskTexture
1-1. 删除CubismMaskControllerInspector.cs的EditorGUILayout.ObjectField
1-2. 将CubismMaskController.cs中序列化的_maskTexture变更为非序列化处理
1-3. 重新创建AssetBundle

case 2: 如果各模型都有一个CubismMaskTexture副本
2-1. 在CubismMasktexture.cs中创建删除_renderTexture的方法
2-2. 在放弃模型的时机调用2-1中创建的方法
2-3. 重新创建AssetBundle

采取上述任一措施都可以解决内存记忆泄漏的问题。

背景/原因

在Live2D模型中对设置剪贴的模型进行AssetBundle化后,从那里读取和卸载模型会导致内存记忆泄漏。

原因是每次读取模型时,都会生成一个蒙版用RenderTexture,即使模型被删除,RenderTexture仍然存在。
在组件上设置的物体是通过物体复制进行AssetBundled化,而不是对该物体的参考。
因此,如果您通过AssetBundle化生成模型,在将CubismMaskTexture的副本GlobalMaskTexture设置到CubismMaskController.MaskTexture时,会在读取模型的同时生成GlobalMaskTexture的复制并创建RenderTexture。
但是,即使删除了模型,RenderTexture仍然没有被删除。结果导致内存记忆泄漏。

由于以下三个原因,此错误的更正没有应用在Cubism SDK中。

  1. 因为根据如何处理蒙版用RenderTexture,对应方法也有所差异
  2. 因为专注于结构清晰和简单的CubismComponents变得复杂
  3. 因为不使用AssetBundle时没有问题

对策

对策的注意事项

本节介绍了两种对策。

请注意,这两个对策是独立的对策,所以不要在一个项目中同时采取两个对策。

对策因您使用CubismMaskTexture的方式而异。只使用一个CubismMaskTexture时,或者为各模型使用一个 CubismMaskTexture副本。
这里将针对两种使用方法,各说明一种对策。

Case 1: 使用一个CubismMaskTexture显示模型时

如果您重复使用一个CubismMaskTexture显示模型,请采取此对策。

首先,删除CubismMaskControllerInspector.cs中包含的EditorGUILayout.ObjectField。

            ・・・
            // Draw mask texture.
            EditorGUI.BeginChangeCheck();


            // 相关部分。删除这一行。
            controller.MaskTexture = EditorGUILayout.ObjectField("Mask Texture", controller.MaskTexture, typeof(CubismMaskTexture), true) as CubismMaskTexture;


            // Apply changes.
            if (EditorGUI.EndChangeCheck())
            {
                EditorUtility.SetDirty(controller);
            }
            ・・・

接下来,将CubismMaskController.cs中序列化的_maskTexture变更为非序列化处理。

    ・・・
    public sealed class CubismMaskController : MonoBehaviour, ICubismMaskTextureCommandSource
    {
        /// <summary>
        /// <see cref="MaskTexture"/> backing field.
        /// </summary>
        [SerializeField, HideInInspector] // 相关部分。删除这一行。
        private CubismMaskTexture _maskTexture;

        /// <summary>
        /// Mask texture.
        /// </summary>
        public CubismMaskTexture MaskTexture
        {
        ・・・

至此即完成了对策。
然后,请重新创建AssetBundle。

case 2: 如果各模型都有一个CubismMaskTexture副本

如果各模型都有一个CubismMaskTexture副本,请执行以下对策。

首先,在CubismMaskTexture.cs中创建一个对_randerTexture进行Destroy的public方法。
以下为代码示例。

        public void DeleteRenderTexture()
        {
            DestroyImmediate(_renderTexture);
        }

在放弃模型的时候调用上述方法。
这样您就可以向模型一样删除RenderTexture。

完成上述对策后,请重新创建AssetBundle。

请问这篇文章对您有帮助吗?
关于本报道,敬请提出您的意见及要求。