MaterialPropertyBlock Increases Draw Call?

Hey,

Apologies if this isn’t strictly shader related but I assume you guys will have had to deal with this alot and thus may be able to answer it for me…

Im not sure if im using the MaterialPropertyBlock property correctly as it seems that if I set MaterialPropertyBlock different for one object than another then it acts like I set a different material and increases the draw calls.

Im using it like the following:

    public class SpriteEffects : MonoBehaviour
    {
        [Range(0, 1)]
        public float greyscaleAmount = 0;

        [Range(0, 2)]
        public float brightnessAmount = 1;
      
        private float lastGreyscaleAmount = -1;
        private float lastBrightnessAmount = -1;      

        private MaterialPropertyBlock props;

        void Start()
        {
            props = new MaterialPropertyBlock();
        }

        void Update()
        {
            if (greyscaleAmount == lastGreyscaleAmount && lastBrightnessAmount == brightnessAmount)
                return;

            lastGreyscaleAmount = greyscaleAmount;
            lastBrightnessAmount = brightnessAmount;

            renderer.GetPropertyBlock(props);
            props.AddFloat("_GreyscaleAmount", greyscaleAmount);
            props.AddFloat("_BrightnessAmount", brightnessAmount);
            renderer.SetPropertyBlock(props);
        }
    }

Anyone any clues?

I tried spying at the SpriteRenderer to see how Unity does it but unfortunately its all internal calls :frowning:

    public sealed class SpriteRenderer : Renderer
    {
        public Sprite sprite
        {
            get
            {
                return this.GetSprite_INTERNAL();
            }
            set
            {
                this.SetSprite_INTERNAL(value);
            }
        }
        public Color color
        {
            get
            {
                Color result;
                this.INTERNAL_get_color(out result);
                return result;
            }
            set
            {
                this.INTERNAL_set_color(ref value);
            }
        }
        [WrapperlessIcall]
        [MethodImpl(MethodImplOptions.InternalCall)]
        private extern Sprite GetSprite_INTERNAL();
        [WrapperlessIcall]
        [MethodImpl(MethodImplOptions.InternalCall)]
        private extern void SetSprite_INTERNAL(Sprite sprite);
        [WrapperlessIcall]
        [MethodImpl(MethodImplOptions.InternalCall)]
        private extern void INTERNAL_get_color(out Color value);
        [WrapperlessIcall]
        [MethodImpl(MethodImplOptions.InternalCall)]
        private extern void INTERNAL_set_color(ref Color value);
    }

When material properties or materials are different between two objects then two objects can’t batched together (with dynamic batch or static batch). Also it results in a Material.SetPass call.

How does the SpriteRenderer do it then? I believe it uses a [PerRendererData] for the _mainTex in the shader and thus you can have thousands of sprites on screen and the draw call count will be one (if they all share the same atlas)…

EDIT: Unless it encodes the sprite offset within the atlas in the vertex data and thus the Matarial’s properties stay the same, its just the vertex data that changes…

In that case is there any way to get at the vertex buffer for the SpriteRenderer? Why is that class sealed?

BTW I appreciate the reply Joachim

Heyo, just looking into draw calls and material property blocks myself. I think I have a reasonable theory to answer this. The sprite renderer generates geometry and UVs against the image data of an atlas, and as evidenced by my work with creating custom shaders that interact with sprite renderer specifically, the colors you adjust actually are deposited as vertex color on this generated mesh.

Because of this, the material is never actually using custom values across different sprites. Only the sprite mesh’s UVs, vertex position, and vertex color change. Hence, assuming the sprite data is referencing the same sprite sheet (and therefore the same material property block value for the sprite atlas on _MainTex), draw calls will batch.

Ballpark? Clear miss?

2 Likes