Draw Call Count와 Batch Count의 차이 (Unity 5)

Unity가 5 버젼으로 넘어오면서 Stats 창에서 Draw Calls 의 갯수를 표현하는 부분이 사라졌다. 대신 Batches와 SetPass Calls 라는 항목이 생겼다. 여전히 Profiler에서는 Rendering 파츠에 Draw Calls가 있지만 Draw Call 대신 Batch를 줄이는라 표현으로 유도하는 의도가 보인다.

스크린샷 2015-04-28 오후 6.33.58

Official Doc에서도 http://docs.unity3d.com/Manual/DrawCallBatching.html 이야기한다. “중요한건 Batch Count야!!!” 라고. 자, 그런데 문제는 Draw Call과 Batch Count와 무슨 차이인가 라는 점이다. 아래에 그 의미가 나와 있다.

 To draw an object on the screen, the engine has to issue a draw call to the graphics API (e.g. OpenGL or Direct3D). Draw calls are often seen as expensive, with the graphics API doing significant work for every draw call, causing performance overhead on the CPU side. However this is not the case. The expensive part is the changing of the resources accessed by the GPU between the draw calls as it triggers a graphics driver validation each time. The emphasis should be on reducing the batch count, rather than the draw call count.

nVidia에서 내놓은 그 유명한 Batch Batch Batch 자료 2페이지를 보면, Batch를 이렇게 정의한다. http://www.nvidia.de/docs/IO/8230/BatchBatchBatch.pdf

Every DrawIndexedPrimitive() is a batch
– Submits n number of triangles to GPU
– Same render state applies to all tris in batch
– SetState calls prior to Draw are part of batch

정리하면, Draw call은 그야말로 Object를 Screen에 Draw 하라는 명령이고, Batch는 각각의 Draw Call 사이에서 그래픽스 드라이버 유효성 체크를 진행할 때, GPU에 의해서 접근되어지는 Resource들을 변경하는 일련의 작업들을 총칭한다.

따라서 하나의 Batch 작업으로 여러번의 Draw Call이 가능한 일이다. 즉, Batch Count는 언제나 Draw Call보다 적게 된다.

실제 유니티 상에서 한번 테스트를 해보았다. 평범한 Cylinder 하나를 그려도 Batch Count와 Draw Call Count는 일치한다. Batch Count가 늘어나면 Draw Call도 늘어나고 Draw Call Count가 늘어났을 경우 Batches 의 갯수도 늘어나 있고 그 숫자 또한 일치한다.

스크린샷 2015-04-28 오후 6.28.42

스크린샷 2015-04-28 오후 6.28.53

Multi-Sub 메쉬를 엔진에 얹어봐도 Batch와 Draw Call 수는 일치하고, Multi Pass Shader를 얹어봐도 Batch와 Draw Call 수는 일치한다. Dynamic Batching이 되는 Object를 얹어도 Batch Count와 Draw Calls 수는 일치하기는 마찬가지이다.

즉, DirectX기준 DrawIndexedPrimitive(), OpenGL로 말하면 glDrawElements() 의 호출이 곧 Batch 라고 말한다. DrawIndexedPrimitive()이나 glDrawElements()의 호출은 Draw Call의 호출이지 왜 이게 Batch인가? 라는 생각이 든다. 이건 Batch와 Draw Call이 개념상 다르지만, 실제로 그 발생 횟수가 거의 비슷하다는 말이다. Batch는 CPU가 GPU에게 건내주는 택배 박스라고 생각하면 이해가 좀 수월하지 않나 싶다. Command Buffer상의 SetState()를 호출하는 작업과 그려질 Triangle의 정보들을 셋팅하는 작업, Rendering State를 셋팅하는 작업들이 다 이에 해당한다. Batching이 다 끝나면 GPU에서 “택배 받으세요” 하고 넘겨준다. 따라서 Batching이 끝나면 Draw Call이 발생하게 된다.

위에서도 언급했지만 Batch Count와 Draw Call 이 반드시 일치하는 것은 아니다. (Link) 그러나 많은 경우 Batch와 Draw Call의 수는 같거나 거의 동일하다. 그래서 실무에선 Batch Count와 Draw Call Count를 같은 의미로 쓰기도 한다.

Unity에서 고정 Object에 대해서 체크를 해주면 Static Batching이, 300개 미만의 Vertex를 가진 Object에 대해서 Dynamic Batching이 발생하지만 동일 Material, 동일 Shader를 쓴다는 가정하에 발생한다. 그리고 Batching이 발생한 후에는 즉시 Draw Call이 발생한다.

따라서, 유니티에서 Draw Call이 없어지고 갑자기 왜 Batch가 들어왔는가? 라는 질문에 대한 답변은 Batching 시에 실제 CPU 부하가 많이 발생하기 때문일 것이다. 그동안은 유니티로 게임 개발하다가 “Draw Call의 갯수 줄이세요~” 라고 이야기 했다면, 이제부터는 “Batch 갯수를 줄이세요~” 라고 해야하겠다. 퍼포먼스적인 면에서 더 정확히 정의하려는 그들의 의도가 좋아 보인다.

Draw Call도 Batch도 항상 가능한 줄여야할 녀석들이라는 점~ ㅎ

관련하여 Unity Forum에서 나눠진 논의들…
http://forum.unity3d.com/threads/draw-calls-vs-batches-optimization-unity-5.318704/#post-2088565