GameView의 Statistics 창을 보면 DrawCall 을 나타내는 지표로 Batches 와 SetPass Calls 가 있다.
아니, DrawCall 횟수가 없네?
사실 몇달전 면접질문 중 하나가 유니티의 DrawCall 을 Batch와 SetPass Call 개념을 통해 설명해보라 였는데 대답을 못했었다.
문득문득 이불킥 감이었는데, 얼마전 오지현님의 URP 관련 동영상 강의를 보면서 개념에 대해 정리하게 되었다.
먼저 DrawCall 에 대해 설명하자면,
CPU가 GPU한테 그려라! 명령을 내리는 작업이다.
보통 그래픽 최적화 시, 프레임당 드로우콜이 몇번 발생했느냐를 따질때 그 드로우콜인데, 유니티에서는
그려라! 라는 것(DrawCall) + SetPass Call 을 Batch 숫자로 환산한다.
자 그럼 이 Set Pass Call 이 무엇인지만 알면 Batch 가 무엇인지 확실히 알게 되는데, 이 개념이 사실 쉽게 와닿지 않았다(최소한 나에겐)
CPU가 GPU에게 내리는 명령은 사실 DrawCall 뿐만이 아니다.
CPU는 GPU에게 명령할때 Command Buffer 라는 곳에 여려 명령들을 모아놨다가, 이 버퍼 자체를 전달하는데 이때 여려 명령들 중에 하나가 드로우 콜이다.
보다시피 드로우콜은 수많은 커맨드 중 하나일뿐이고, 특정 커맨드들을 묶어서 Set Pass Call 로 명명한다.
왜 얘네들만 따로 묶어서 명명하지?
그 이유는 저 커맨드들이 GPU에 부하를 주는 주범이기 때문
이 커맨드들의 공통점은 GPU에 상태값을 전달하는 명령이라는 것인데, 만약 같은 메테리얼을 가진 오브젝트 10개를 그린다고 가정했을때 첫번째 그리는 오브젝트에서 DrawCall + Set Pass Call 이 호출되고나면 두번째 오브젝트부터는 DrawCall 만 GPU에 전달하면 된다.
왜냐면 텍스쳐나, 쉐이더 상수, 쉐이더 코드, 블렌딩 공식등 (Set Pass Call 에 해당하는 명령들) 은 첫번째 오브젝트 그릴때 전달했던 값을 그대로 쓰면 되니까
하지만 만약 다른 메테리얼을 가진 오브젝트를 그려야 하면 이전에 전달한 값을 그대로 쓸수 없을 것이니 저 Set Pass Call 에 해당하는 명령들을 GPU에 다시 전달해야 할것이다. 문제는 저 명령들이 GPU 입장에서 비용이 많이 드는 작업이라는 것.
그럼 위 결과를 토대로 본다면, 저 Set Pass Call 을 줄일수록 GPU에 주는 부하가 적게 들것이고, 이는 곧 최적화와 연결된다.
Set Pass Call 을 최대한 적게 호출하면서 Draw Call을 수행하는 것.
유니티에서는 이를 배칭 이라고 표현한다.
자 이제 아래의 Statistics 창에서 왜 Batches 와 Set Pass Calls 이 표현되는지 이해가 가는가?
우리의 최적화의 최종 목표는 Batches 에 비해 Set Pass Calls 를 최대한 줄이는 것 (Batches - SetPass Calls 값이 적을수록 최적화가 잘 되었단 의미)
그럼 어떻게 하면 SetPass 를 줄일 수 있죠?
- 일단 단순히 메테리얼을 적게 쓰면 된다. (= 메테리얼 베리언트 수를 적게 유지할것)
- 메테리얼 교체 비용이 많이 발생하지 않도록 renderer 옵션의 sorting layer 를 통해 동일한 메테리얼들을 하나로 묶어서 그리게 한다
- Dynamic 배칭과 Static 배칭을 적극 활용한다 (이 두가지 배칭에 대한 개념 및 차이는 본 블로그 주제와 다르므로 설명 생략)
- 하지만 만약 Dynamic 배칭이 일어날 확률이 극히 적을 경우엔 아예 Dynamic 배칭 기능을 끄는게 좋다고 한다
- Dynamic 배칭 대상인지 아닌지 확인하는 작업도 비용소모가 발생
- 하지만 만약 Dynamic 배칭이 일어날 확률이 극히 적을 경우엔 아예 Dynamic 배칭 기능을 끄는게 좋다고 한다
위 내용을 기반으로 다른 문제를 하나 더 생각해보자
만약 서로 다른 메테리얼이지만 동일한 쉐이더를 쓰는 경우를 가정해보자 (A 메테리얼의 색깔은 빨간색, B 메테리얼의 색깔은 파란색으로 하고 싶다)
A 메테리얼 - Shader A
B 메테리얼 - Shader A
이 경우 Batch 와 SetPass Call 은 각각 2 개로서 동일한 쉐이더를 쓰지만 둘은 서로 메테리얼로 인식해서 배칭이 이루어지지 않을 것이다.
하.지.만 유니티 에서는 SRP Batcher 기능을 켜면 위와 같은 상황에서 배칭이 된다 (!!!)
원리는 간단한데, GPU 메모리에다가 저 SetPass Call을 통해 전달받은 값을 저장해놨다가, 나중에 동일한 값이 들어왔을때 그 값을 바로 참조해서 쓰는 것이다. 이에 대한 자세한 개념은 위에 소개한 동영상에서 확인 할 수 있다.
단점은 저 값을 저장해야 하니까 GPU 메모리를 먹는다는 것인데, 효과에 비한다면 엎드려 절이라고 하고싶은 기능이다.
'Unity' 카테고리의 다른 글
Quaternion 공간변환 (0) | 2020.11.23 |
---|---|
rigidbody, collider 개념 및 정리 (0) | 2020.10.08 |
IL2CPP (0) | 2020.09.29 |
Addressables (0) | 2020.07.28 |
Mobile 최적화 (0) | 2020.06.12 |