Search Unity

  1. Unity 2020.1 has been released.
    Dismiss Notice
  2. We are looking for feedback on the experimental Unity Safe Mode which is aiming to help you resolve compilation errors faster during project startup.
    Dismiss Notice
  3. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

Learn how to use MaterialPropertyBlocks and [PerRendererData] for great performance gains!

Discussion in 'Scripting' started by Thomas-Mountainborn, May 25, 2016.

  1. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    351
    Hey there!

    I found that while MaterialPropertyBlocks are very useful, the documentation on them is sparse at best. I've come to use them quite often, so I've decided to write about them in the hope that someone will find the information of use. You can read the article on my blog, right here. Spoiler: 15 milliseconds are shaved off of rendering time in my sample scene. Have fun animating materials the efficient way!
     
    Last edited: May 25, 2016
  2. Stephan-B

    Stephan-B

    Unity Technologies

    Joined:
    Feb 23, 2011
    Posts:
    2,269
    Thank you for taking the time to write about MaterialPropertyBlocks and good job :)

    MaterialPropertyBlocks are a very powerful feature which as you pointed out, poorly documented and unknown by most.

    MaterialPropertyBlocks are not available with the CanvasRenderer which is sooo sad.
     
    Thomas-Mountainborn likes this.
  3. Henning_Justare

    Henning_Justare

    Joined:
    Nov 10, 2014
    Posts:
    21
    "MaterialPropertyBlocksare not available with the CanvasRenderer which is sooo sad"
    Just found out about MaterialPropertyBlocks and then it's not available where I need it.. :(

    Dont want a new instance of the material for each panel in my gui, and the draw calls to match.

    Any way to get around it, seams like Image does something like it, with different color and sprite without triggering drawcalls? ... Is it creating a rendere of somekind behind the scenes?
     
    mdrunk and mpearson729 like this.
  4. ArachnidAnimal

    ArachnidAnimal

    Joined:
    Mar 3, 2015
    Posts:
    1,696
    Material blocks are also used extensively in the courtyard demo:
    They are used on the moving spheres ("Agents") that move around and bump into you.
    The use them for setting the emissionColors. Example code:

    Code (csharp):
    1.  
    2.  
    3. private PropertyBlock m_PropertyBlock ;
    4. private Renderer myRenderer;
    5.  
    6. void Start()
    7. {
    8.      myRenderer = GetComponentInChildren<Renderer> ();
    9.      m_PropertyBlock = new MaterialPropertyBlock ();
    10. }
    11.  
    12. void Update()
    13. {
    14.  
    15.        m_PropertyBlock.SetColor ("_EmissionColor", newColor * 7.8f);
    16.        myRenderer.SetPropertyBlock (m_PropertyBlock);
    17. }
    18.  
    It is being used on renderers using the built in standard shader
    I havent tried it out but do you really need to create your own shader?
     
    Last edited: Jun 18, 2016
  5. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    351
    You have to explicitly have the [PerRendererData] attribute on the shader property. If it's not there, SetPropertyBlock will work, but Unity secretly creates a material instance in order to do so, giving you the same result as renderer.material (I also mention this in the blog post, FYI).

    Sadly, I don't know the answer to this either. I imagine Unity has a good reason for not being able to use them in CanvasRenderers.
     
    NathanJSmith likes this.
  6. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,951
    <3

    Unity has this tendency to create a lot of really neat stuff, and then never tell anybody about it.
     
  7. Hodgson_SDAS

    Hodgson_SDAS

    Joined:
    Apr 30, 2015
    Posts:
    123
    Downside to this is you have to create a custom material inspector to set anything in the editor.
     
  8. tswalk

    tswalk

    Joined:
    Jul 27, 2013
    Posts:
    1,108
    I was just curious, not reading everyone else's comments but... since you are doing that in Update, not all calculated transitions of the color and subsequent are applied during a frame. Would it be so perhaps less CPU time used on that update, if you instead set it in a coroutine to do the new lerp color and setpropertyblock on waitforendofframe?
     
  9. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    351
    There is a performance gain to be had when not using Update() on a very large group of objects, but using coroutines is apparently up to 5 times slower than using a custom update function. Unity has written a post about that subject here. (search for "coroutine" to find the developer comment on them)

    It should also be noted that in your example, you are using WaitForEndOfFrame - if you yield return a new instance of this in the coroutine, you will end up creating a very large heap of garbage objects that have to be collected, creating performance spikes. It's best practice to return null when waiting for a single frame in a coroutine.
     
    Last edited: Jan 27, 2017
  10. Joboto

    Joboto

    Joined:
    Sep 12, 2013
    Posts:
    64
    Thanks for this, very useful to know about it.
     
    Thomas-Mountainborn likes this.
  11. CaseyLee

    CaseyLee

    Joined:
    Jan 17, 2013
    Posts:
    37
    should be noted that using a manager to call many custom Update() functions [ex: "UpdateMe()"] instead of using Unity's Update() individually for many objects - is the best way to do things for a large group of instanced objects. Also if you are using a manager there is no need to consider the overhead of calling unity's "Update()" vs. a co-routine, as you will now only be calling it once. In the discussion of deciding on 100+ Update() calls on instances of object or instead calling separate co-routines for each of those instance, just thought was a crime not to mention object pooling. [as noted in prev. posted article]

    Also most important thing ... using the update instead of a custom co-routine insures the graphical changes your making to the material happen once per rendered frame, as the Update() function is in sync with unity's Render-pipeline loop. +the added benefit of using deltaTime as an option.
     
  12. ryan_unity

    ryan_unity

    Unity Technologies

    Joined:
    Dec 28, 2014
    Posts:
    11
    Hi,

    Even though this post is quite old, I thought I'd chime in to help resolve some possible confusion here regarding usage of the PerRendererData attribute.

    The PerRendererData attribute is purely UI related and has no impact on performance. It used to (~Unity 5.1) show the texture from the Renderer inside the Material Inspector, but this is no longer the case. Now it currently has the same behavior as the HideInInspector attribute, except it may help people reading the shader better understand that the property is set via a MaterialPropertyBlock...

    I've submitted a report to our documentation team so this can be explained in the docs. I've also submitted a bug report so this can be investigated by our developers, perhaps to either remove this attribute, or add some functionality to it.

    I hope this helps!
     
  13. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    351
    Could've sworn that at the time of writing without the attribute, a new material instance was created nonetheless. I'll update the article.
     
  14. nawash

    nawash

    Joined:
    Jan 29, 2010
    Posts:
    164
    Hi guys,
    Great post. Thanks.
    Just as Henning_Justare, I am here trying to use MaterialPropertyBlock on CanvasRenderer using a shader I'm writing for Image.
    Is there any workaround for using MaterialPropertyBlock on CanvasRenderer ?
    Thanks
     
    mdrunk likes this.
  15. nawash

    nawash

    Joined:
    Jan 29, 2010
    Posts:
    164
    Same here :(
    Are you aware of a solution for this ?
    Thanks
     
    mdrunk likes this.
  16. LucasRizzotto

    LucasRizzotto

    Joined:
    Dec 11, 2015
    Posts:
    20
    Reviving this thread to ask a question! I've been using MaterialPropertyBlocks in Unity and noticed that while it's avoiding Materials from being instanced, it's breaking batching.

    Is this normal? Are these draw calls " less expensive " because they aren't coming from material instances? Or should I not be seeing all these draw calls?
     
  17. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    351
    I'll quote the end of the article:

     
  18. codestage

    codestage

    Joined:
    Jul 27, 2012
    Posts:
    1,396
    sanmn19 likes this.
  19. YondernautsGames

    YondernautsGames

    Joined:
    Nov 24, 2014
    Posts:
    199
    Just to check. If I set a texture property in a property block, does that mean copying all the texture data to the graphics card every time I set that property block? Should I be using 2 property blocks instead since I only set the texture once, but some simple properties every frame
     
  20. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    351
    The texture data will be sent to the GPU in a draw call, not when changing the texture property in a material property block. The material property block is just a way of organizing which data to send to the GPU when executing a draw.
     
  21. mdotstrange

    mdotstrange

    Joined:
    Sep 23, 2013
    Posts:
    242
    So what is the current state of affairs as far as performance trade-offs when working with property blocks?

    It seems they stop memory from being allocated each time when used vs using Renderer.material or Renderer.materials?

    But unless you use them with custom shaders as explained in this tweet posted above then they push up the drawcall count?

    What do you gain by using PropertyBlocks with the StandardShader? Memory vs drawcalls?

    Thanks.
     
  22. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    4,951
    I mean, profile it, but they should be faster than other ways of swapping individual settings on a renderer.

    If you have many different renderers all using a few variants of the same material, you should just have those materials and then swap the .sharedMaterial of the renderers.
     
  23. won-gyu

    won-gyu

    Joined:
    Mar 23, 2018
    Posts:
    1
    Thanks for the nice article!
    Now I got it why "MaterialPropertyBlocks and [PerRendererData]" is strong.
    But is there a case where I shouldn't use it but just StandardShader?
    And I just wonder, in your article, since you use MaterialPropertyBlocks with [PerRendererData] attribute, it reduces CPU overhead. When I look closely at screenshots you gave, the one using [PerRendererData] gets rid of SetPassUncached which seems to cause the CPU overhead. Could you elaborate it? I couldn't find a good reference about SetPassUncached.

    Running a short test, I noticed that maybe you already know, it does batch together when your different mat property blocks become the exactly same, and it break dynamic batching again when those become different.
     
  24. AlejUb

    AlejUb

    Joined:
    Mar 11, 2019
    Posts:
    25
    Is Per-Renderer Data and Instanced Property somehow related?
    Matrices for example can't be assigned as shader lab properties, so it's not possible to tag them 'per renderer data'.
    I guess I could fill the matrices by hand using 4 float4's (or three and expanding the 0,0,0,1 on my own), but is there a better way? property blocks and instanced property also works without duplicating the material under the hood?
     
unityunity