Search Unity

What's the deal with material?

Discussion in 'Scripting' started by ValBis, Feb 12, 2021.

  1. ValBis

    ValBis

    Joined:
    Jul 9, 2017
    Posts:
    50
    Hi, I am trying to understand how Unity deals with materials. I got from the Documentation (and tested) then when the Rendered.material property is changed, a new material is created in its place. But I was trying to understand what happens if we first create a new material, then assign it to the renderer, and then we try to destroy that material directly.

    This is important in projects where you create new materials and textures at runtime and you wanna make sure it doesn't have memory leaks.

    This is an example file you can put on any empty scene:
    Code (CSharp):
    1. using UnityEngine;
    2. using System;
    3. using System.Collections;
    4. using System.Collections.Generic;
    5.  
    6. namespace AsImpL
    7. {
    8.     namespace Examples
    9.     {
    10.         public class Example : MonoBehaviour
    11.         {
    12.             private void Start()
    13.             {
    14.                 StartCoroutine(LoadObjects());
    15.             }
    16.  
    17.             public IEnumerator LoadObjects()
    18.             {
    19.                 Debug.Log("BEFORE CREATING CUBE");
    20.                 yield return new WaitForSeconds(3);
    21.  
    22.                 GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
    23.                 Debug.Log("AFTER CREATING CUBE");
    24.                 yield return new WaitForSeconds(3);
    25.  
    26.                 Material newmat = new Material(Shader.Find("VertexLit"));
    27.                 Debug.Log("CREATE MATERIAL");
    28.                 yield return new WaitForSeconds(3);
    29.  
    30.                 cube.GetComponent<Renderer>().material = newmat;
    31.                 Debug.Log("SET MATERIAL");
    32.                 yield return new WaitForSeconds(3);
    33.  
    34.                 Destroy(cube.GetComponent<Renderer>().material); // DOESN'T FREE!
    35.                 Debug.Log("DESTROY MATERIAL 1");
    36.                 yield return new WaitForSeconds(3);
    37.  
    38.                 Destroy(newmat);
    39.                 Debug.Log("DESTROY MATERIAL 2"); // FREE!
    40.                 yield return new WaitForSeconds(3);
    41.  
    42.             }
    43.         }
    44.     }
    45. }
    run the script and check the profiler, in the "Memory" tab. When I run
    new Material
    a new material is created. Then I destroy the material (in the two different ways).
    The first way does not result in actually freeing the memory for that material, (even though the material does disappear and the cube appears pink as expected). The second way actually frees the material, as I can see from the Memory tab.

    This is very puzzling. Isn't
    Destroy(cube.GetComponent<Renderer>().material);
    actually referencing to the material itself?

    In practice I know this can be freed by
    Resources.UnloadUnusedAssets()
    but I want to understand better why my DESTROY 1 doesnt' work.
     
    Last edited: Feb 12, 2021