I am trying to change the color of multiple dynamic identical meshes using the same (Simple Lit) material with GPU instancing enabled without breaking the SRP Batcher/GPU instancing. If I don’t change anything, they get batched just fine, however, when a property is set via MaterialPropertyBlock both GPU instancing and SRP Batcher break. The Frame Debugger says “Non-instanced properties set for instanced shader.”
I think it works as when I set the same color for all of them (eg. Color.red), they get batched. However, the same happens with SRP Batcher disabled. There needs to be a way of changing some material properties of an instanced shader.
You can define properties in the CBUFFER UnityPerMaterial or UnityPerObject (see the “Constant Buffers and UnityPerMaterial” section of the following link:)
thanks for this @JohnKP-Mindshow . What is UnityPerObject used for? I’ve never seen it before and can’t find anything about it. As for UnityPerMaterial, I already use that, my shaders are 100% SRP Batcher compatible (the inspector says it as well). The problem is that each SRP batch has a lot of draw calls although all objects use the same material with different MaterialPropertyBlock BaseColor. I wonder how this was done . Maybe it’s just a pool of predefined materials and each object picks one when initialized. https://www.youtube.com/watch?v=pUM7ifjGKkM
IIRC, UnityPerObject are like the name says; values that can be set per mesh. I.E. if you have a bunch of different meshes all using the same material, you could have each mesh tinted a different color (still using the same material).
Whereas if you have UnityPerMaterial, its variables that are specific to a material. So if you have a variety of meshes all using the same material and you would like to tint all of them the same color, thats where the property should live
SRP and set many colors with MPB are not compatible.
Maybe you can instantiate many materials on playing and change color of each.They will be batched by SRP batcher.
SimpleLit’s BaseColor with MPB and GPU instancing are also not compatible.
The BaseColor must be moved from UnityPerMaterial CBUFFER to UNITY_INSTANCING_BUFFER to support GPU instancing with the colors.
That UNITY_INSTANCING_BUFFER thing and SRP batcher are not compatible, basically.
So Unity will automatically Draw them without SRP batcher.
Draw with SRP batcher and Instancing and many colors must be done manually with BatchRendererGroup or something.
It’s worth noting that changing properties of a material through MBP works in the sense that the material updates, but It behaves as if it created a new one and changed that property as it counts as a new drawcall.
My old approach was to have each object instantiate the original material in the Awake() function, but the results were the same. While the objects were grouped in one SRP Batch, that SRP Batch had as many draw calls as there are objects.
UNITY_INSTANCING_BUFFER_START() doesn’t work with SRP at all. I get an error can’t find _BaseColor if I try to use it, even with SRP Batcher disabled.
I’ll set up a small project when I have some time to hopefully illustrate the issue more clearly.
It would be really great to know more about best practices on this and how, if possible, to reproduce the kind of functionality MPBs afford only using SRP batcher-compatible properties. In my use case, I’m having to animate values on materials which I’ve been doing with MBPs up 'til now.
I’ve got some time to set up a small repo to emphasize the issue I’m having. either I’m doing something wrong, or the SRP Batcher doesn’t work properly.
After some more research, I found out that SRP Batcher doesn’t save draw calls, it only makes them faster. So basically it’s better to clone the original material and change its properties, as opposed to using MPB. In order for that to work, the GPU Instancing needs to be disabled on the original material.
// EDIT
So the problem I was having with my custom shader was that the UnityPerMaterial CBUFFER had too many properties (increasing its size). The Frame Debugger will say that it can’t be batched because “Objects have different materials” which is misleading.
Below is some sample code with the current working CBUFFER properties to make an idea of how big it should be.
I found you can abuse some of the builtin properties from UnityPerDraw to supply your own data. In my case I never use Enlighten dynamic lighting, so I just set my values to renderer.realtimeLightmapScaleOffset
Note that lightmapIndex or realtimeLightmapIndex must be > 0 (and < 65533), or it won’t update scale/offset. Which is okay, but also if you use mixed lighting, your lights won’t recognize objects with these values as shadowcasters. But then you can create non-mixed lights and use rendering layers to filter with them instead…
It’s a mess.
Can you elaborate a bit on this hack?
If I understood it right, you can set per frame data via Renderer.realtimeLightmapScaleOffset (Vector4) and than access this data as a float4 in a vert/frag shader without creating a separate material instance and breaking SRP batching, right?
What is the field / equivalent in Unity for this field, unity_LightmapST?
Yes, you can set it per-renderer and it won’t break SRP batching.
In the shader you can access it via float4 unity_DynamicLightmapST which can be a part of UnityPerDraw cbuffer.
Omg this is genius. I wonder, does Unity apply UnityPerDraw cbuffer built-in properties in a core library automatically somehow? Because it tells me in the inspector that my shader is SRP Batcher compatible even without declaring a UnityPerDraw cbuffer section / property.
Hi, I’m trying to find all the ways to manually push data in through the existing UnityPerDraw properties, but I’m struggling with the lack of documentation. Have you found any other properties that are available to use for custom perdraw data? I’d love to use unity_ProbeVolumeWorldToObject 4x4, or some of the Spherical Harmonic float4 properties, but I can’t figure out how to access them in script.
I just want to get a single matrix4x4 into each object, but it completely breaks SRP Batching, whether via material property block or material instances (causing a multitude of SRP batches of size 1, which for all intents and purposes is broken). I can’t find a way that doesn’t kill performance.
You can probably declare a global matrix array as a StructuredBuffer (Shader.SetGlobalBuffer) and use unity_DynamicLightmapST.x as an index to it? That’ll work, although it’ll be an additional indirection.
Are you using that Hack in Bakery? I wonder if i will be able to use Bakery AND also use this hack to do some per Renderer Shader Tricks.
It’s really frustrating that Unity doesnt provide a way to do stuff like this more easily. I mean they poured so many resources into the Scriptable Renderpipeline to give more control, but failed to offer easier ways for doing something as obviously useful like setting PerRenderer Data while being able to batch draw calls. If Unity ever comes around to making the engine open source (which all the main big competitors do), i’ll throw a big party and you’re all invited <3 !