Hi all!
We’ve been playing around with using the URP with our game which is a 2D platformer using SpriteRenderers. We have a single material for almost every sprite in the game backed by a single uber shader. Previously, we were using MaterialPropertyBlocks to change the properties from object to object. However, we noticed this caused problems with the existing dynamic batching. That is, the dynamic batcher will not batch objects with different MPBs even though they share the same shader and material.
When I read up on the SRP Batcher, I got excited because it seemed to support our use case of a single shader with slight variations. However, we ran into a number of issues. First, it didn’t seem like there was a way to achieve the same programmatic runtime effect as we were achieving with MPBs. We could dynamically instantiate thousands of materials at runtime but that seemed a little crazy. We could run an editor script to do the same but it also seemed like a regression.
More importantly, though, it appears that the SRP Batcher does not support SpriteRenderers at all. Is this the case? The documentation is somewhat vague but seems to imply that only Meshes are supported. On top of that, we could not get the SRP Batcher to batch a single group of SpriteRenderers even in minimal test scenarios.
So, having struck out with both Dynamic Batching and the SRP Batcher, we started brainstorming additional options. I’m hoping someone in this forum might have some ideas. I’m about ready to rip the rest of my hair out. This leads to the ultimate question:
Is there any way with the new URP to specify per-object or per-material values programmatically that don’t prevent batching?
(I don’t believe instancing is viable here because of the constraints related to meshes which are out of our control because they’re handled by the SpriteRenderer.)
Addendum
One thing that got us tantalizingly close was to create a StructuredBuffer of our material data for all objects and index into this at runtime inside the shaders. I’m not a shader programmer so I’m not sure if this is a big faux pas, but I’ve been able to hack this together. However, the big problem we encountered is there’s no convenient way to specify what index per object draw the shader should look at for its data. Unity already does a lot of this internally with stuff like unity_ObjectToWorld but it doesn’t seem like we have access to any similar capabilities. In the absence of this, we’ve tried two methods to funnel our data through Unity’s existing tooling:
-
Store the data in Renderer.renderingLayerMask
This works like a charm! It gets passed to our shader asunity_RenderingLayerand we can get the int index out viaasuint. Unfortunately, objects with different renderingLayerMasks are never batched for some reason. Is this intended behavior? Is there a way around it? -
Store the data in one of the 4 members of Renderer.lightmapScaleOffset
This also works. We setlightmapIndexto 0 as well otherwise it doesn’t get passed to the shader. Unfortunately, dynamic batching refuses, saying simply “Objects are lightmapped.” We got the idea from https://forum.unity.com/threads/provide-a-per-object-index-for-each-vertice.827937/ where @DerHumpink implied they were able to get it working. Is there any way to convince the dynamic batcher that it should batch these objects despite seeing Vector4(4, 0, 0, 0) vs Vector4(5, 0, 0, 0) in lightmapScaleOffset?
Does anyone have any ideas? Really desperate here as the draw call and SetPass overhead is killing us.