Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    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:
    501
    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

    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.
     
  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?
     
  4. ArachnidAnimal

    ArachnidAnimal

    Joined:
    Mar 3, 2015
    Posts:
    1,761
    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:
    501
    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.
     
    OMOH98 and NathanJSmith like this.
  6. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,294
    <3

    Unity has this tendency to create a lot of really neat stuff, and then never tell anybody about it.
     
    GordonNB, OMOH98, ckimport and 16 others like this.
  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.
     
    DryerLint likes this.
  8. tswalk

    tswalk

    Joined:
    Jul 27, 2013
    Posts:
    1,109
    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:
    501
    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:
    41
    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:
    501
    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:
    166
    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:
    166
    Same here :(
    Are you aware of a solution for this ?
    Thanks
     
    mdrunk likes this.
  16. LucasRizzotto

    LucasRizzotto

    Joined:
    Dec 11, 2015
    Posts:
    27
    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:
    501
    I'll quote the end of the article:

     
  18. codestage

    codestage

    Joined:
    Jul 27, 2012
    Posts:
    1,931
    DuncanAbrakam, deus0 and sanmn19 like this.
  19. YondernautsGames

    YondernautsGames

    Joined:
    Nov 24, 2014
    Posts:
    352
    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:
    501
    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. imaginationrabbit

    imaginationrabbit

    Joined:
    Sep 23, 2013
    Posts:
    349
    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:
    6,294
    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.
     
    Alic likes this.
  23. won-gyu

    won-gyu

    Joined:
    Mar 23, 2018
    Posts:
    33
    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?
     
  25. Long2904

    Long2904

    Joined:
    May 13, 2019
    Posts:
    81
    So do I still need to add the [PerRendererData] now? Does it still secretly create a new material if I don't add it? If it does then why should I use it? Also, does the material property block still work in URP and shader graph (shader graph doesn't seem to support per-instance properties)?
     
  26. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    501
    No, PerRendererData is purely visual and will simply hide the material property from the inspector. I can't say anything about URP, I have not touched the SRPs.
     
  27. Long2904

    Long2904

    Joined:
    May 13, 2019
    Posts:
    81
    So does the material property block still secretly create new material? I saw you mentioned it here.
     
  28. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    501
    I don't believe it does, no.
     
  29. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,817
  30. codestage

    codestage

    Joined:
    Jul 27, 2012
    Posts:
    1,931
    petey likes this.
  31. petey

    petey

    Joined:
    May 20, 2009
    Posts:
    1,817
  32. Alvare32

    Alvare32

    Joined:
    May 26, 2020
    Posts:
    59
    Great stuff. MaterialPropertyBlock is a really powerful function. I used it for this specific case.
    In Ocarina Of Time, there are multiple Gerudo woman roaming around and they all share the same eye texture. My first iteration of the, let's call it the 'Actor Blinking' script, grabbed a reference to the eyes material asset directly and applied the texture change to it like it was a sharedmaterial.
    gla_eye01_CI00.png gla_eye02_CI00.png gla_eye03_CI00.png
    But this caused a problem. Now each actor blinking script would essentially blink for every woman on screen. This caused them all to be rapidly and simultaneously blink.
    With this materialproperty function, all I had to do it set the eye texture through like this:
    Code (CSharp):
    1. meshRendererEye.SetPropertyBlock(mpbEye, eyeMaterialIndex);
    You can even make it work on meshes with multiple materials.
     
  33. CaseyHofland

    CaseyHofland

    Joined:
    Mar 18, 2016
    Posts:
    610
    Nooooo the link is broken!

    I was wondering if anyone would happen to know how performance scales with the Animation Window. Animations and MaterialPropertyBlocks seem to work together, which is great, but I wonder if it's best practice. Our game has street lanterns, and right now we have simply created an animation that animates the material emission to turn off and on during night and day time respectively.

    Thing is, since animating affects the material property block, and thus a single renderer, right now we animate all the streetlamps individually through the virtue of prefabbing.

    Should I be worried? We have a 100 streetlamps now, but a 1000 tomorrow, and I'd venture to guess that simply updating 1 shared material would be much better. But I dunno, they all use the same animation and I am unsure if Unity's parallization already takes care of it.

    (As I was writing this I figured that, really, this is a question for the animation forums).
     
    ModLunar likes this.
  34. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    501
    Oh yeah, I took the site offline because I moved it to a different host and haven't bothered to put it up again, sorry!

    For your use case, I'd just animate the shared material. If you want to offset the time at which they're flickering, you could just do that in the shader by offsetting the emission value with the world position.
     
  35. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    374
    Aw dang haha, the site is down for me too today.

    I had this topic (MaterialPropertyBlocks) on my backlog of topics to learn, and today I set out to learn it -- but am now out of luck as well! Any way you could share the gist of your blog post here? :)
     
  36. CaseyHofland

    CaseyHofland

    Joined:
    Mar 18, 2016
    Posts:
    610
    I found something different when I was looking for this, I might as well post it here now: it's a really good tutorial, Ronja ain't no fool.
    https://www.ronja-tutorials.com/post/048-material-property-blocks/
     
    ModLunar likes this.
  37. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    374
    Ah thank you!

    Actually in that one they're using GameObjects -- but I'm only drawing with Graphics.DrawMeshInstanced/RenderMeshInstanced(...).

    I was actually just about to post, I found another one too which will help in my case!
    https://toqoz.fyi/thousands-of-meshes.html
     
    CaseyHofland likes this.
  38. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,919
    You do know about the Wayback machine, right? Archive.org? Here's the blog post. Note that Archive.org is a bit slow. But since it's essentially the backup of the whole internet and it's for free, don't complain ^^.
     
    DeathPro likes this.
  39. ModLunar

    ModLunar

    Joined:
    Oct 16, 2016
    Posts:
    374
    Oh wow, that's a lovely suggestion, thanks!

    Interesting.. I guess they literally added 2500 MonoBehaviours (their "SphereWithMaterialPropertyBlock" class) in their example.. doesn't that mean 2500 MaterialPropertyBlocks?