공부/Unity

[최적화 스터디] 4. 드로우 콜, 배칭, SRP Batcher

BeepMaeae 2025. 11. 27. 15:42

* 본 게시물은 3D 유니티 프로젝트를 다룬다.

 

Draw Call

CPU가 상태 세팅(머티리얼, 셰이더, 텍스처)을 마친 이후 드로우를 호출하면, GPU가 픽셀 연산을 수행하게 된다.

이때 CPU가 GPU에게 상태 세팅을 보내서 연산을 수행하라는 명령 1번을 Draw Call(드로우 콜)이라고 한다.

주의할 점은 오브젝트 수가 아닌, 묶을 수 있는 오브젝트 그룹 수가 draw call의 수다. 오브젝트가 같은 머터리얼, 같은 렌더링 상태를 보유하고 있다면 같은 그룹으로 묶을 수 있다.

 

Unity의 Frame Dubber를 사용하면 Draw call을 분석할 수 있다.

Frame Debugger 실행 화면
해당 화면에서 Draw Call은 Mesh repulse_mesh, Mesh, warehouse_light_001, Dynamic Dustparticles로 총 3개다.

CPU가 매 프레임마다 draw call을 많이 던지면, draw call을 셋업하다가 병목나는 경우가 많기 때문에 최대한 줄여주는 것이 좋다.

아래 방법들로 draw call을 줄일 수 있다.

 

Static Batching

Static Batching 움직이지 않는 Mesh들을 빌드 타임에 큰 덩어리로 합쳐서, 런타임엔 하나로 그려버리는 방식이다.

오브젝트를 선택한 뒤, static 옵션을 선택하면 사용할 수 있다.

GameObject에서 Static 체크

장점:

  • 같은 머티리얼을 쓰는 정적인 오브젝트를 매우 크게 묶을 수 있다.
  • CPU draw call의 수가 많이 줄어든다.

단점:

  • 메모리를 많이 차지한다.
  • Transform가 변경되어 움직이게 된다면 내부적으로 다시 처리해야 해야 한다.
  • Runtime에 생성되는 오브젝트는 Static Batching 대상이 되려면 설정과 조건이 까다롭다.

 

그렇다면 정적인 것만 최적화할 수 있을까? 꼭 그런 것만은 아니다.

 

Dynamic Batching

Dynamic Batching매 프레임 CPU가 작은 메쉬들을 모아서 임시로 하나의 버퍼에 복사해서 묶어서 그려주는 방법이다.

이런 방법이 있다니, 그렇다면 Static/Dynamic Batching을 활용하면 Draw Call을 극한으로 줄일 수 있겠구나 ... 하고 생각하기에는, Dynamic Batching에는 조건도 많고, 그 한계도 많다.

 

조건

  • Mesh vertex count가 작아야 한다.
  • 스케일이 너무 복잡하거나 조건이 맞지 않으면 배칭이 불가능하다.

장점:

  • Static이 아닌 오브젝트들도 어느 정도 묶을 수 있는 방법이 있다는 것에 의의가 있다.

단점:

  • CPU가 매 프레임 vertex 데이터를 재복사해서 묶기 때문에, 오히려 CPU 오버헤드가 커질 수 있다. 따라서 작고 많은 오브젝트에만 의미 있고, 고성능 프로젝트에선 잘 사용하지 않는다.

 

GPU Instancing

GPU Instacning동일한 메쉬와 머터리얼을 사용하는 여러 오브젝트를 한 번의 드로우 콜로 묶어서 렌더링하는 기술이다. 원하는 머티리얼에 Enable GPU Instancing 체크하면 사용할 수 있다.

Material에서 Enable CPU Instancing 체크

장점:

  • CPU는 draw call 몇 개만 던지고, GPU가 인스턴스별 위치와 등을 알아서 처리해준다.
  • Dynamic Batching과 달리 CPU가 vertex 복사를 하지 않고, GPU가 인스턴스 데이터를 받아 처리하므로 훨씬 효율적이다.

단점:

  • 완전히 다른 머티리얼이면 여전히 다른 draw call이다. 그래서 처음에는 GPU Instancing을 모든 머터리얼에 체크해주는 것이 무조건 좋은 게 아닌가 생각했는데, 오히려 다른 머터리얼이면 GPU Instancing을 사용하기 위한 세팅 시간만 늘어나 비효율적으로 바뀐다.

GPU Instancing은 다음 예시 코드와 같은 상황에 사용하면 가장 좋을 듯하다.

// 예시 코드 (MaterialPropertyBlock으로 색 바꾸기):
var renderer = GetComponent<MeshRenderer>();
var block = new MaterialPropertyBlock();
renderer.GetPropertyBlock(block);
block.SetColor("_BaseColor", someColor); // URP Standard Lit 기준
renderer.SetPropertyBlock(block);

 

SRP Batcher

SRP BatcherURP/HDRP 같은 SRP에 도입된 머티리얼/셰이더 상태 셋업을 최적화하는 시스템이다.

이전 방식들은 오브젝트마다 셰이더 상수(유니폼/상수 버퍼)를 세팅하는 데 CPU 오버헤드가 컸는데, SRP Batcher는 이걸 셰이더 단위로 묶어서 캐싱해준다.

같은 셰이더를 쓰는 오브젝트들은 SRP Batcher가 알아서 묶어서 CPU가 GPU로 상태를 보내는 과정을 최소화해준다.

URP Asset 설정에서 SRP Batcher을 켜면 사용할 수 있다. 단, SRP Batcher 지원 셰이더를 사용해야 SRP Batcher를 사용할 수 있다.

 

유의할 점은, 이전 방식들(Batching)은 메시/머티리얼 수준의 묶기지만, SRP Batcher는 셰이더/상수 버퍼 세팅을 효율화한다.

즉, 둘은 서로 다른 레벨에서 작동하므로 적절하게 두 방식 모두 사용하면 최고의 성능을 낼 수 있따.

 

아래는 4가지 방식을 대략적으로 정리한 표이다.

기법 개념 장점 단점

기법 개념 장점 단점
Static Batching 안 움직이는 Mesh들을 빌드 타임에 큰 덩어리로 합쳐서 그리는 방식 같은 머티리얼 쓰는 정적 오브젝트는 드로우콜을 크게 줄일 수 있음 합쳐진 메시 때문에 메모리 사용량 증가
Dynamic Batching CPU가 매 프레임 작은 Mesh들을 모아서 임시 버퍼에 복사해 한 번에 그리는 방식 정적이 아니어도, 조건만 맞으면 드로우콜을 줄일 수 있음. 코드/세팅 없이도 조건 충족 시 자동으로 작동 CPU가 매 프레임 vertex 복사를 해서 오히려 CPU 부담이 커질 수 있음
GPU Instancing 동일한 메쉬와 머터리얼을 사용하는 여러 오브젝트를 한 번의 드로우 콜로 묶어서 렌더링하는 기술 draw call 수를 크게 줄이면서도 CPU 오버헤드가 작음. Dynamic Batching보다 현대적인 방식, 대량 오브젝트에 잘 맞음 머티리얼이 달라지면 배칭이 깨짐. MaterialPropertyBlock 사용 필요
SRP Batcher URP/HDRP에서 셰이더별 상수 버퍼 세팅을 최적화해 draw call당 CPU 셋업 비용을 줄이는 시스템 draw call 개수를 줄이지 않더라도 CPU 렌더링 시간 감소, 셰이더 단위로 묶어서 상태 변경 비용을 크게 줄임. Static/Instancing과 병행 가능 (다른 레벨에서 최적화) Scriptable Render Pipeline(URP/HDRP)에서만 사용 가능

 

이미 해당 프로젝트에서는 URP를 사용하고 있기 때문에 꽤나 괜찮은 최적화가 되어 있다고 볼 수 있다.

추가로 Static Batching과 GPU Instancing을 적절히 활용해준다면 최고의 Batching 환경을 만들어줄 수 있으리라 기대해본다.

'공부 > Unity' 카테고리의 다른 글

[최적화 스터디] 3. Project Auditor 2  (0) 2025.11.27
[최적화 스터디] 2. Project Auditor  (0) 2025.11.21
[최적화 스터디] 1. GC에 대해  (0) 2025.11.18