Batching and Sprite Renderers

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 as unity_RenderingLayer and we can get the int index out via asuint. 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 set lightmapIndex to 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.

Did you ever end up finding a solution to this? I’m currently working on a project and in almost the same boat as you, albeit with far less experience and absolutely no shader programming knowledge. I’m curious what the work around looks like in terms of time investment as it’s only for a small project for school, but I still wonder because I’d be interested in working with URP in the future, but not if this is a recurring issue.

same problem, any updates / approaches?

same problem, any solution now? My 2D URP project has a lot of SpriteRenderers to draw, but I find that both SRP Batch and GPU Instance are’t support for SpriteRenderer, so how can I do something for this components? Manually change SpriteRenderer to other component, just as Quad?

@llxwd008 SpriteRenderer.color has 32 bits available, won’t break dynamic batching, and you can put a coordinate in there to index into other data.

If I recall right, @Tony_Max was working on a more custom solution (Graphics.DrawMeshInstancedProcedural), so perhaps he might share.

Yep, i was working on instanced sprite renderer for DOTS and i’ve finally got working solution, but it has no documentation yet, just raw unity git package wich i use for internal needs. I can share, but it requires a huge foundation to initialize all data right, which i have also, but it a bit ugly and works mostly localy for my project, that is why main package doesn’t include it. Anyway DOTS sprite rendering system

Thank you for share very much, I’ll try it carefully.

My DOTS sprite rendering solution is now documented and compatible with latest entities release. discussions thread

1 Like

I just find this repository as a solution for certain situation. Hope it can help.

It is 2025 and I am sure there may be other posts about this issue but I was wondering what is current status on this issue?

Using Unity 6 and 2d renderer sprite rendering, what is the way to do per renderer property without breaking batching?

Still no official way to this from Unity?

Use SRP Batching on the latest (6.1). It’ll say a lot of calls but they won’t be as expesive as traditional batches.