GrabPass + Dynamic Batching == Friendship 🤜🤛

Update: the original trick was not working with Unity prior to 2017.2
But it appeared that the fix is quite simple and now it works fine with the latest versions (tested with 2019.2). Source code is inside.

GrabPass is probably the easiest way to get the content of the screen and pass it to the shader automagically. Later you can use this texture to make some nifty effects like distortion and reflection.

But GrabPass has a huge fault: it breaks the dynamic batching 💔

So why Unity3d stops batching meshes with GrabPass in their material? Frame Debugger is the tool we need. Let’s dig in.

FrameDebug to the rescue!

Aha. It appears Unity3d can’t batch meshes with multi-pass shaders. And that has some sense. Except that GrabPass is not a real shader pass. It’s just a command to render pipeline to grab the content of the screen and save it as texture for further use. Nothing is being rendered at that time.

So what can we do to have awesome GrabPass-based effects and not to suffer from lots of draw calls?
Just remove GrabPass from our shader!

“Whaaaat? But where shall we get the snapshot of our screen?” you could ask.
Easy! If you take a look at documentation one more time you may notice the following detail:

GrabPass { “TextureName” } grabs the current screen contents into a texture, but will only do that once per frame for the first object that uses the given texture name. The texture can be accessed in further passes by the given texture name. This is a more performant method when you have multiple objects using GrabPass in the scene.

And this means that we can access this texture in any further pass of any shader used on any object!

That’s why we remove the GrabPass from our shader and move it to another dedicated shader, the only purpose of which is to capture and store the content of the screen. Then we need to use this shader with some dummy Renderer. Probably the easiest way is to put it right on the main camera, so it’s always “visible” (surely it should not be visible by the eye, but it must be rendered to make the GrabPass work).

Single draw call!

Of course, this method has some drawbacks:

  • First of all, you have to remember to have a dummy object with a special GrabPass shader.
  • The second one is that you’ll need to have a separate GrabPass shader for each unique texture of the screen. E.g. if you need to grab the background and foreground parts of your scene, you’ll need to have two shaders, the only difference of which is the name of the GrabPass.
  • The last but not least, you have to remember to have the name of the GrabPass and the name of the texture variable synced.

But I believe that all these downsides are really nothing comparing with capability to have dynamic batching and hence higher performance.

This trick was discovered while working on our upcoming game Summer Catchers. Don’t hesitate to subscribe to our newsletter and participate in futher beta-testing to see those crystals and enjoy the entire gameplay ;)

Share Comments
comments powered by Disqus