Unity/유니티 공부
유니티 - 드로우 콜과 배칭
tita
2024. 12. 5. 14:26
드로우 콜(Draw Call) 과 배칭(Batching) 은 렌더링 성능에 중요한 개념입니다. 이를 이해하면 게임 최적화에 도움을 줄 수 있습니다.
우선 드로우 콜에 대해 알아보겠습니다.
드로우 콜(Draw Call)
드로우 콜은 GPU에게 "화면에 무언가를 렌더링하라"고 요청하는 CPU의 명령입니다.
- 각 드로우 콜은 특정 객체나 메시에 대해 렌더링 파이프라인을 초기화하고 데이터를 GPU로 전송합니다.
- 드로우 콜의 수가 많아지면 CPU가 GPU에 렌더링 요청을 처리하는 데 시간이 오래 결려, 성능 저하가 발생할 수 있습니다.
드로우 콜 증가 요인
- 서로 다른 재질(Material)을 사용하는 오브젝트
- 서로 다른 메쉬(Mesh)를 사용하는 오브젝트
- 동적으로 움직이는 오브젝트(애니메이션이나 변형)
- 조명 및 그림자 효과를 적용받는 오브젝트
드로우 콜의 개념에 대해 익혔으니 어떤 순서로 명령이 일어나는지 알아보겠습니다.
[CPU와 GPU의 상호작용]
CPU에서 GPU에 명령하기
- 일반적으로 CPU에서 렌더링, 상태 변경 등의 명령을 GPU에 전달합니다.
- 하지만 GPU가 작업 중이라면, CPU는 GPU의 작업이 종료되기까지 대기해야 합니다.
- 따라서 커맨드 패턴(Command Pattern) 과 메시지 큐(Message Queue)에 의한 비동기 방식을 활용합니다.
- CPU 에서 GPU에 전달할 명령을 임시 공간에 담아두고, GPU가 여유 될 때 명령어를 하나씩 꺼내서 처리합니다.
Command Buffer(커맨드 버퍼)
- CPU의 각 스레드에서는 GPU에 전달할 렌더링 관련 명령어를 모듈화하여 커맨드 버퍼에 쌓아 저장해 놓습니다.
- 그리고 GPU의 공유 커맨드 큐에 전송한 뒤, GPU를 기다리지 않고 다른 작업을 수행할 수 있습니다.
- 각 스레드마다 커맨드 버퍼를 가지고 공유 큐에 전공이 가능하므로, 멀티스레드를 활용한 병렬 처리에도 유용한 방식입니다.
- 명령을 곧장 커맨드 큐에 전송하는 것은 데이터 동기화 문제로 인한 성능 저하를 유발할 수 있으므로, 명령들을 모아 버퍼 단위로 전송하는 방식을 사용합니다.
데이터 동기화 문제로 인한 성능 저하
GPU와 CPU는 서로 다른 하드웨어이기 때문에 작업을 처리하는 속도와 방식이 다릅니다.
CPU가 GPU에 명령을 곧바로 전송하면 다음과 같은 문제가 발생할 수 있습니다.
1. 동기화 오버헤드 발생
- CPU가 명령을 GPU에 곧바로 전송하면, GPU는 명령을 처리하는 동안 추가 명령을 대기해야 할 수 있습니다.
- CPU도 GPU의 작업 상태를 확인하며 동기화를 맞춰야 하므로, GPU가 바빠지면 CPU도 대기해야 합니다.
- 이 과정에서 동기화 오버헤드가 발생하여 두 하드웨어 모두의 효율이 떨어집니다.
2. 명령 전송 간격의 비효율
- 명령이 한 번에 작은 단위로 전달되면, GPU는 자원을 비효율적으로 사용할 수 있습니다.
- 예를 들어, GPU는 병렬 처리를 위한 대량의 데이터를 동시에 처리하도록 설계되었는데, 한 두개의 명령만 전달하면 병렬 처리의 이점을 활용하지 못하게 됩니다.
3. 스레드 충돌 가능성
- 여러 스레드에서 명령을 동시에 GPU에 보낸다면, 명령 간 순서를 보장하기 어렵고 데이터 무결성을 유지하기 위한 추가 작업이 필요합니다. 이 역시 성능 저하를 유발할 수 있습니다.
Command Queue(커맨드 큐)
- GPU에는 CPU와 GPU가 함께 공유하는 커맨드 큐가 존재합니다.
- GPU는 여유가 될 때마다 커맨드 큐에 담긴 커맨드 버퍼를 차례로 꺼내어, 명령을 처리합니다.
[드로우 콜(Draw Call)]
Render State(렌더 상태)
- GPU가 렌더링을 수행하기 위해 필요한 정보들
- 쉐이더, 메쉬, 텍스쳐, 알파 블렌딩 옵션, ZTest 및 스텐실 옵션 등이 있습니다.
Render State Changes(렌더 상태 변경)
- GPU에서 렌더링할 대상의 상태가 변경될 경우 수행됩니다.
- CPU가 GPU에 렌더 상태 정보를 전송하며 상태 변경 명령을 보냅니다.
SetPass Call
- 쉐이더로 인한 렌더 상태 변경만을 의미합니다.
- 쉐이더 내의 Pass 변경, 쉐이더(메테리얼) 자체의 변경, 쉐이더 내 파라미터들의 변경에 해당됩니다.
- 메쉬의 변경은 렌더 상태 변경이지만, SetPass Call 에는 해당하지 않습니다.
- 따라서 메쉬가 달라도 메테리얼이 같다면 SetPass Call은 딱 1번만 발생합니다.
DP Call(Draw Primitive Call)
- CPU가 GPU에 그리라는 명령을 직접적으로 호출하는 것(예: glDrawElement())
- 그려야 할 대상의 정보가 조금이라도 달라지는 경우, DP Call에 앞서 렌더 상태 변경이 동반됩니다.
- GPU는 현재 렌더 상태 정보를 기반으로 렌더링을 수행합니다.
Draw Call
- CPU에서 GPU에 렌더링 명령을 전송하는 것을 의미합니다.
- Render State Changes + DP Call 과정을 통칭합니다.
[배칭(Batching)]
Batch
- SetPass Call + DP Call
- Render State Changes 가 아니라 SetPass 를 포함하므로, Draw Call 보다 좁은 의미를 가집니다.
- Batch가 적다고 Draw Call 자체가 적은 것이 아닐 수 있습니다.
- Batch는 메쉬 상태 변경을 포함하지 않으므로, 배칭 처리가 되었더라도 메쉬가 다야하면 실제로 Draw Call은 훨씬 많을 수 있습니다.
- Batch가 메쉬의 변경을 포함하지 않는것은 중요합니다. 메쉬가 달라도 메테리얼이 같으면 하나의 Batch로 통합할 수 있습니다.
Batching
- 여러 개의 Batch를 하나로 묶는 최적화 기법
Static Batching
- 배칭의 한 종류
- 런타임에 움직이지 않는 메쉬들에 대해서만 가능합니다.
- 여러개의 매쉬를 하나의 메쉬로 통합합니다.
- 매쉬를 통합한 만큼, 하나의 배치로 그려줄 수 있습니다.
- 인스펙터에서 Bathcing Static 플래그를 설정하면 됩니다.
- Static Batching이 되더라도 컬링 연산은 원래의 메쉬 기준으로 이루어진다는 장점이 있습니다.
- 컬링 연산이 원래 메쉬 기준으로 이루어지면 얻는 이점
1. 불필요한 오브젝트의 렌더링 방지
- 컬링(Culling)은 카메라에 보이지 않는 오브젝트를 렌더링하지 않는 최적화 기법입니다.
- Static Batching 에서는 여러 메쉬를 하나로 통합하더라도, 컬링은 통합 전 개별 메쉬 기준으로 이루어집니다.
- 즉, 배치된 매쉬 중 일부만 카메라에 보이는 경우에도 보이는 메쉬만 렌더링하고, 보이지 않는 메쉬의 렌더링 비용은 발생하지 않습니다.
2. 세밀한 렌더링 제어 가능
- 개별 매쉬 기준으로 컬링이 이루어지면, 복잡한 지형이나 환경에서 더 세밀한 제어가 가능합니다.
- 예를 들어, 넓은 환경에서 빌딩 여러 개를 Static Batching으로 묶었다고 가정하면 :
- 빌딩 중 하나라도 카메라에 보이는 경우 해당 매쉬 전체를 렌더링하는 대신, 카메라에 보이는 빌딩만 렌더링하고 나머지는 제외할 수 있습니다.
3. 레이캐스팅 등 물리 연산에 영향 없음
- Static Batching 이 적용되더라도 메쉬 기준으로 컬링이 이루어지므로, 물리 연산(예 : 레이캐스팅)은 배칭 전의 개별 매쉬 기준으로 동작합니다.
Dynamic Batching
- 배칭의 한 종류
- 유니티에 의해 내부적으로 자동으로 수행됩니다.
- 플레이어 설정에서 Dynamic Batching을 켜주면 됩니다.
- 실제로는 제약이 많으므로 Dynamic Batching에 의한 최적화를 기대하기는 힘듭니다.
- 예를 들어, 스킨 메쉬에 대해서는 적용되지 않으며 Vertex 300개 이상의 메쉬에 대해서도 적용되지 않습니다.
GPU Instancing
- 배칭의 한 종류
- 동일 메쉬, 동일 메테리얼인 경우에만 가능합니다.
- 쉐이더에서 인스턴싱 사용 여부를 명시해주어야 합니다.
- 쉐어더에서 Instancing Buffer 영역을 선언하여 인스턴싱을 적용할 쉐이더 파라미터를 설정할 수 있습니다.
- 메테리얼에서 Enable GPU Instancing에 체크하여 인스턴싱을 적용할 수 있습니다.
- 배칭이 적용됨에도 불구하고 Material Property Block을 통해서 메테리얼마다 파라미터를 변경할 수 있다는 장점이 있습니다.
SRP Batcher
- 배칭의 한 종류
- 유니티의 Scriptable Render Pipline에서만 동작합니다
- 동일 쉐이더, 다른 메테리얼에 대해서도 적용됩니다.
- 쉐이버 베리언트에 대한 특정한 규칙을 만족하는 경우에만 적용될 수 있습니다.