19
\$\begingroup\$

Instancing improves performance (significantly) when rendering multiple (hunders? thousands?) copies of the same mesh at once. But how much overhead does it have when rendering exactly one copy with one draw call? Would it be a good or a bad idea to use instancing for all geometry rendered by engine?

Edit: Let's say we're creating an FPS game. Most of the objects have only one instance: a knife, a gun, a machine gun, a building and a radio tower. But there are also some objects with multiple instances: trees (e.g. 3 kinds of trees with hundreds of instances), grass and so on... What I mean is: instead of rendering the one-instance objects the "traditional" way and trees and grass using instancing, we render all of them using instancing. So our radio tower has only one instance (whose information we store in an instance data buffer) and we render that tower using some kind of DrawInstanced() call with instance count equal 1. Same with all other objects (of course, trees and grass have multiple instances).

So my question is: is it a bad idea to draw a single instance of an object using instancing? Does instancing have too much of an overhead (memory- and performance-wise) or is it in any way undesired for rendering single-instance objects?

\$\endgroup\$

4 Answers 4

20
\$\begingroup\$

Under D3D9 with XPDM you almost certainly want to instance wherever possible. Draw call overhead is just so high that it makes sense. In that scenario the crossover-point can be as low as 2 or 3 instances.

If you've only got one instance of a given mesh, it may on the surface seem tempting to draw it non-instanced. However, look at what's involved:

  • You need to switch from loading data into a per-instance buffer to uploading shader constants.
  • You need to keep two copies of your vertex shader: one for instanced and one for non-instanced.
  • You need to keep two copies of your rendering code: likewise.
  • You need to switch shaders.
  • You need to switch vertex formats.
  • You may need to switch vertex buffers.
  • And then you need to do it all over again if you're going back to instanced drawing for the next group of meshes.

Even if you've only got a single mesh (like the gun model in an FPS) there are cases where instancing is useful. Let's say you're doing multipass light accumulation in a forward renderer and with a z-prepass. Instead of an extra pass for each light, you make your lights per-instance data and you draw it instanced.

Based on the first scenario, the moral of the story is that if any object class can use instancing at all, it makes sense to always use instancing for all objects of that class.

Based on the second scenario, the moral of the story is that instancing can have non-obvious uses that go beyond just saying "I need to draw 20 trees".

\$\endgroup\$
1
  • \$\begingroup\$ The reasons you gave are the very same ones that made me ask this question in the first place. Thank you. \$\endgroup\$
    – NPS
    May 4, 2014 at 18:06
12
\$\begingroup\$

(On my system, didn't test it anywhere else) In GL, instancing a single mesh (drawing with count = 1) has some nasty overhead, but I don't know where it comes from. I strongly suggest not doing it.

I tested this in a practical application a couple of months ago. I coded some global illumination algorithms in the Crytek Sponza scene, which consists of roughly 350 or so meshes (don't remember exactly), of which a couple share a handful of instances. At the beginning I did it like you suggest, just instance everything and draw the rest with instance count of 1, since it simplified the rendering code a bit.

Later when optimizing the renderer, just switching back from instancing the count=1 objects to submitting them the usual way saved me about 3.5 milliseconds per frame worth of time on an i7 3770k (and GTX 770). Switching the meshes with multiple instances to just doing them the traditional way saved me another 0.5ms. Overall the application went from ~120 FPS to about ~230 FPS.

These figures of course always depend on where the bottlenecks are in your application, and the latter 0.5ms might actually become a slow-down in an application where you're very draw-call bound. But otherwise, in my experience instancing has some nasty overhead if you're not drawing a lot of things at once.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Interesting, but it'd be nice to see data for AMD and Intel drivers as well, otherwise, you really should say "On my system" instead of "In GL". On the other hand, even if it's not an issue with other implementations, the fact that it could be on some is reason enough to avoid instancing if you're not using it. \$\endgroup\$
    – bcrist
    May 5, 2014 at 0:20
2
\$\begingroup\$

You can be for certain that drawing a single object instanced is more expensive than drawing a single object normally. For instancing the GPU is preparing for a large amount of objects and this preparation will be different than for a single object. However how large this performance gap is can only be found by experimenting and is very much depending on your actual rendering set-up. The only way to know for sure is by testing it yourself. Benchmarking a single draw call is hard here are several ideas on how you could proceed.

\$\endgroup\$
2
  • \$\begingroup\$ "For instancing the GPU is preparing for a large amount of objects..." No, you pass in the number of objects you're going to draw up front. There may be some overhead, but not because the GPU overestimates the number of instances you're wanting drawn. \$\endgroup\$ Dec 15, 2021 at 13:13
  • 1
    \$\begingroup\$ I didn't mean preparing to mean estimating/imprecise here. Just that it needs to allocate buffers and keep track of how many things it has already drawn. \$\endgroup\$
    – Roy T.
    Jan 11, 2022 at 8:44
2
\$\begingroup\$

It's been 4 years... and I think its safe to say that is perfectly fine to submit "instanced" draw calls with 1. As you may have noticed, new APIs DX12 and Vk both have an instance count that can be from 0 to NUM_INSTANCES. Also note that there is no DrawIndexed(...).

EDIT

As a note of caution, the above is probably fine with this modern APIs, maybe using something old like Gl<3.3 or perhaps DX11 will require some profiling as mentioned by other users.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .