유니티 엔진 (Unity Engine)

유니티 그래픽스 최적화 - 4. 드로우콜과 배칭 (Draw Call & Batching)

원소랑 2019. 9. 4. 19:48
728x90

.

.

 

4. 드로우콜과 배칭 (Draw Call & Batching)

 

4-1 드로우콜 (Draw Call)

 

수많은 병목지점이 있지만, 드로우콜이 병목인 경우가 많다. 최적화를 많이 간과하기 때문.

 

01) 드로우콜의 이해

CPU가 GPU에 오브젝트를 그리라는 명령을 호출하는 것.

한 프레임에서, 오브젝트 하나를 그릴 때 여러가지 정보들이 CPU에서 GPU로 전달됨.

(매시 데이터 ,텍스쳐, 쉐이더, 트랜스폼, 블렌딩 셋팅 등등)

DATA > Storage(HDD,SDD,SD) > CPU Memory > GPU Memory

 

플랫폼에 따라 VRAM(GPU Memory)은 자동으로 관리됨. 가비지는 VRAM 에서 제거하고 올리는 등 관리가 됨. GPU는 그려야 하는 상태 정보를 담는 테이블을 가지고 있고 텍스처 슬롯들, 렌더링 모드 상태들, 알파 블렌딩, Z 테스트 등의 정보를 담음. = 렌더 상태(Render States)

 

Render States 테이블은 VRAM 의 각각 데이터들의 메모리 주소를 저장.

 

CPU 가 Render States 변경을 GPU 로 보내 저장하고

CPU가 마지막으로, GPU에 메시를 그리라는 명령을 보낸다.

= Draw Primitive Call = DP Call

 

오브젝트 메시를 헨더링 하고 다음 오브젝트 렌더링 준비.

Mesh 변경, Shader, Texture 변경 명령 등등. 변경할 필요 없는 상태는 유지함.

 

매번 오브젝트 렌더링할 때 Render State Changes 가 일어남. CPU 는 오브젝트 하나 렌더링 할 때 Render State Changes 명령을 보내고 DP Call. 렌더링 돼야하는 모든 오브젝트들에 반복.

 

위 내용에 따라 오브젝트를 한 번 그리는데 필요한 처리를 묶어서 드로우콜(Draw Call)

(Render States Changes > DP Call)

 

CPU가 GPU로 바로 명령을 내리지 않고, Command Buffer 큐를 거친다.

(Render States Changes, DP Call 커맨드들이 여기 포함)

 

ㅁ 그래픽스 API마다 구체적인 사항이 다를 수 있다.

Vulkan 은 커맨드 버퍼가 여러 개. 그래서 멀티쓰래드로 처리.

여러개의 Command Buffer -> 단일 Command Queue -> GPU

 

Metal 도 비슷한 컨셉으로 멀티쓰레드 병렬 처리.

그래서 Vulkan 과 Metal 이 기존 OpenGL ES보다 Draw Call 에 대한 부담이 적다.

 

OpenGL, DirectX 등의 그래픽스 API들은 CPU에서 GPU로 보내는 명령을 공통 API로 구성함. Graphics API 가 호출되면 드라이버 칩셋에 알맞은 신호로 GPU에 맞게 명령을 해석/변환 과정을 처임. CPU 바운더리 오버헤드가 발생. Draw Call 은 CPU 오버헤드.

 

그래서 멀티스레딩으로 렌더링 관련 커맨드를 전달하면 CPU 오버헤드가 줄어듬.

Edit > Project Settings > Player 에서 Other Settings 의 Rendering 항목에 Multithreaded Rendering 옵션.

디바이스에서 지원하면 동작.

 

GUP성능이 아닌 CPU성능에 의존적이기 때문에, 텍스쳐 크기 줄여도, 폴리곤 수 줄여도 개선되지 않음. Draw Call 횟수를 줄여야 함.

 

02) 드로우콜의 발생 조건

기본적으로 오브젝트 하나 = 메시 1개, 머터리얼 1개 = 1 Draw Call

즉, Batch = 1

 

매시가 하나처럼 보여도 여러 오브젝트로 이루어진 경우도 있음. 어셋의 메시가 17개라면 17번의 드로우콜이 필요. 단, 머터리얼도 17개인 경우와, 머터리얼은 1개인 경우는 성능에 차이가 남.

메시 17개로 이루어진 캐릭터 10마리가 등장하면 170번 드로우콜이 발생.

 

매시 1개 머터리얼이 여러개인 경우도 드로우콜 2개 발생.

 

쉐이더 내에서 멀티패스(Multi Pass)로 두 번 이상 렌더링을 거치면 두 번의 드로우콜 발생.

 

데스크톱에서는 DC 1000넘어도 가능하지만, 모바일은 DC 100도 많은 편. 최신 디바이스는 DC 200도 가능. 단, 기준을 한 마디로 정리하는 건 어렵. 또, DC 하나당 비용을 단언하기도 어렵.

 

03) Batch & SetPass

드로우콜이 포괄적인 개념이다 보니, 유니티에서는 Batch와 SetPass라는 용어로 나누어 표시해준다.

Game View 우상단 Stat 창에서도 Batches 와 SetPass Calls 가 표시.

유니티 프로파일러의 Rendering 섹션에서도 SetPass Call 등을 확인 가능.

 

Batch

DP Call 과 상태 변경들을 합친 넓은 의미의 드로우콜. SetPass는 렌더상태 변경의 발생 여부. (메시 변경은 미포함)

 

Batch가 10번, SetPass 1번이면

= 10번의 드로우콜 동안 쉐이더 변경 없음.

+ 메시 및 트랜스폼 정보 등 최소한의 상태 변경만.

 

Batch가 10번, SetPass 10번

= 10번의 드로우콜 동안 쉐이더가 매번 변경.

+ 경우에 따라 많은 상태 변경들 동반.

 

SetPass call

상태 변경이 많을 수록 성능 비용이 소모. 오브젝트들 렌더링 중 머터리얼을 그대로 쓰면 상태변경 횟수를 줄일 수 있다. 오브젝트 렌더링 중 머터리얼이 바뀌어 쉐이더 및 파라미터들이 바뀌면 SetPass 증가. SetPass 는 쉐이더로 인한 렌더링 패스 횟수. 같은 Batch 횟수라도 SetPass 가 늘어나면 성능에 중요.

 

서로 다른 메시라도 동일한 머티리얼을 사용한다면 SetPass 는 1. Batch는 늘지만 SetPass 는 유지. 이런 걸 Batch 구성이 잘 되어있다 할 수 있다.

 

4-2 배칭 (Batching)

01 배칭의 이해

Batching은 드로우콜을 줄이기 위한 작업. 렌더상태변경+DP Call = Batch. 여러 Batch(=드로우콜)를 하나로 묶어서 하나의 Batch 로 만드는 것이 배칭.

 

ㅁ머티리얼을 기준으로, 다른 오브젝트, 다른 메시라도 같은 머터리얼인 경우, 배칭을 통해 하나의 Batch로 만들 수 있다. 오브젝트가 3개이지만, 같은 머터리얼이면 Batch 3개를 1개로 줄임.

 

텍스처를 아틀라싱 해서 하나의 범용 메터리얼로 만들어 쓰면 하나를 공유하기 용이. 똑같은 쉐이더, 똑같은 텍스처라도 어셋이 다르면 배칭되지 않음. 어셋까지 동일한 머티리얼 이어야 함. 텍스처의 크기 문제도 고려해야함. 구형 디바이스에서는 2048 이상 크기 텍스처는 문제가 생길 수 있음.

 

단, 스크립트에서 Renderer의 머티리얼 속성을 수정하면 머티리얼의 복사본이 생성됨.

 

배칭은 Static Batching 과 Dynamic Batching.

 

02 스태틱 배칭 (Static Batching)

정적인 오브젝트를 위한 배칭 기법. 주로 배경. Inspector 에서 Static 체크박스를 켜주면 로딩 타임에 자동 배칭 됨.

https://docs.unity3d.com/Manual/DrawCallBatching.html

 

Batching Static 플래그가 체크되면 스태틱 배칭의 대상이 된다. Static 체크 받스 옆의 드롭다운 버튼을 누르면 플래그 리스트 오픈. Batching Static 플래그 체크하면 대상이 됨.

 

다이나믹 배칭보다 효율적. 버텍스 연산을 런타임에 수행하지 않음. 런타임동안 움직이지 않는 오브젝트들은 가능한 Static 플래그를 켜줘서 스태틱 배칭의 대상으로 설정해줘야 함.

 

테스트.

씬을 만들고 추가적인 드로우콜 요인을 제거.

- 카메라의 Clear Flags 를 Solid Color 로

- Allow HDR과 Allow MSAA 를 끈다.

- Directional Light 의 Shadow Type은 No Shadows로 설정해서 그림자를 끈다.

오브젝트 추가하고 Static 체크

기본 오브젝트들은 보두 동일한 머티리얼(Default-Material)

 

Game View 의 Stat 에서 Batch 가 4가 아닌 1인 걸 확인.

단, 스태틱 배칭은 메모리가 추가로 필요. 배칭 처리는 드로우콜을 줄이기 위해 옵젝트들을 합쳐서 내부적으로 하나의 메시로 만들기 때문에 배칭 대상이 되는 오브젝트들을 합친 것 만큼의 추가 메모리 필요. 거대한 메시라면 메모리가 더 필요.

버텍스 수나 라이트맵 여부에 등 조건에 따라 두 개 이상의 메시로 나뉘기도 함. 프레임 디버거로 확인.

 

새로운 하나(혹은 여러개)로 만들어 렌더링을 하니 드로우콜이 줄어듬. 메모리를 희생해서 성능을 올린다.

런타임 중에도 Static 배칭을 구성할 수 있음.

 

StaticBatchingUtility.Combine() 런타임 상에 추가된 스태틱 오브젝트들도 배칭 처리. 원래는 씬에 존재하던 오브젝트들만 스태틱 처리 됨. 단, ~.Combine() 콜은 부하가 생김. 데이터 수집하고 메시를 재생성 해야하기 때문.

 

스태틱 배칭의 이점을 활용해서 맵은 모듈화 방식으로 제작 가능. 성능상 유리. 하나의 통째 매시라면 화면에 일부만 보이더라도 전체 처리를 해야하기 때문에 불리.

 

스태틱 배칭 처리가 되더라도 원래의 게임오브젝트 기준으로 컬링. 따라서, 유니티에서 모듈 파츠들을 조합하여 씬을 만들고 스태틱 배칭을 이용하는 것이 효율적.

 

예제 던전은 4개의 배치. Light Mode:Bake, Baked GI 사용, Lightmap Resolution : 27, 스태틱 배칭 사용

실제 오브젝트는 459개가 넘지만 스태틱 배칭의 효율 살려서 4번의 드로우콜로 처리.

 

Saved by batching 은 배칭으로 절약한 드로우콜. 460이 넘어감. 머터리얼이 1개라고 Batching 이 1이 아니고 4가 되는 건, 라이트맵, 라이트 프로브, 동적 라이트 영향 여부 등 다양한 조건에 의해 배칭이 나눠질 수 있음.

 

실제 에셋스토어 배경 모델들도 이러한 방식으로 제작됨. 인하우스 맵들은 모듈화 방식 이점이 높은 경우가 많다. 단, 아웃도어 씬은 조금 다름. 모듈의 단위를 너무 작게 나누면 오히려 비효율적. 반복되는 모듈이 많지 않고

 

모듈화 방식의 에셋 제작 기법 = 책, [모바일 3D 아티스트를 위한 유니티 RPG 게임 배경 스타트업]

 

03 다이나믹 배칭 (Dynamic Batching)

동적으로 움직이는 오브젝트들끼리 배칭처리 하는 기능. Static 플래그가 체크되어 있지 않은 오브젝트들이 대상. 동일한 머티리얼을 사용하고 특정 조건들을 만족하는 다이나믹 오브젝트들은 자동으로 배칭 처리. 유니티 엔진이 알아서 해줘서 특별한 처리는 필요 없다. 플레이어 설정에서 Dynamic Batching 플래그만 켜주면 됨.

 

단, 다이나믹 배칭은 런타임에 처리하기 때문에 제약 사항이 많다. 매 프레임, 씬에서 Static 플래그 없는 오브젝트를 모아서 버텍스들을 모아 합친다. 매번 데이터 구축과 갱신이 발생하기 때문에 오버헤드 발생하지만, 드로우콜을 줄일 수 있어 전체적인 성능 향상.

 

그래서 제약이 많아 다이나믹 배칭에 비해 잘 활용되지 않는 경향.

1. Skinned Mesh 적용 불가. 움직이는 캐릭터 Skinned Mesh, GPU나 SIMD에서 고속연산 수행하기 때문에 다이나믹 배칭으로 묶으면 CPU 효율이 떨어짐.

2. 버텍스가 너무 많은 메시는 다이나믹 배칭에서 제외. 너무 많은 버텍스를 수집하면 오버헤드가 드로우콜보다 높아져서 제외됨. 포지션, 노말, UV를 사용하는 모델이라면 300 이하의 버텍스를 가진 모델만 다이나믹 배칭 적용 가능. (모바일게임에서도 300은 보통 넘기 때문에 적용 어려움)

 

스키닝 (Skinning)

스켈레탈 애니메이션이 적용되지 않은 기본 형태의 메시를 혀재의 애니메이션 포즈에 맞게 변형하는 과정.

T-Pose 모델 > 스켈레탈 구조 연결(= 리깅 Rigging) > 연결 결과는 모델의 임포트 세팅 창에서 Rig > Configure 를 통해서 확인

 

애니메이션 포즈에 맞게 메시의 버텍스 위치들을 보정해주면서 메시가 변형되는 과정 = “스키닝”

애니메이션이 적용되는 메시는 스키닝이 이루어져야 하므로 오브젝트의 Mesh Rendere는 Skinned MEsh Renderer 라는 특수한 Mesh Renderer 가 적용됨.

 

렌더링 전에 스키닝 연산으로 버텍스 위치 재계산. 스키닝 메시 폴리곤 많아지면 연산 부담도 동반. 스키닝 연산은 CPU에서 처리. GPU에서 처리할 수도 있음. (Edit > Player Settings > Player > Other Settings > GPU skinning) GPU 연산이 더 빠르진 않음. CPU 스키닝 연산시 SIMD(Single Instruction Multiple Data)아키텍처를 이용 고속연산 수행. 게임이 GPU 병목인 상황이라면 CPU 스키닝으로 처리하는 것이 더 나은 선택. 병목이 CPU 라면 GPU 연산으로. 대부분은 CPU 연산으로 처리. 프로파일링 필수.

 

단, GPU 스키닝 연산은 OpenGL ES2 에서는 지우너되지 않음. DX11과 OpenGL ES3만 유효. 즉, Metal, Vultkan 에선 GPU 스키닝 미지원. 지원 여부는 바뀔 수 있다.

 

파이프라인, 일반적으로 메시 렌더링 시, 버텍스 쉐이더에서 월드스페이스로 변환. 즉, GPU 고속 연산. 다이나믹 배칭은 버텍스 월드스페이스 변환 연산을 CPU에서 처리. 드로우콜보다 더 많은 오버헤드.

 

드로우콜은 그래픽스 API 영향이 커서 Metal 이나 Vulkan 은 기존 OpenGL ES 보다 드로우콜이 더 빨라서 다이내믹 배칭의 오버헤드가 크면 효율이 더 떨어짐. 그래서 제약이 많다.

 

https://docs.unity3d.com/Manual/DrawCallBatching.html

 

특정 오브젝트의 배칭 오버헤드가 크다고 판단되면 배칭이 되지 않게 강제할 수 있음. 쉐이더 태그에 DisableBatching 플래그를 True 로 설정해주면 처리됨.

 

04 Mesh.CombineMeshes

스태틱 배칭, 다이나믹 배칭 외에 수동 배칭.

동일한 머티리얼 사용 메시끼리 스크립트로 합쳐주는 것. Mesh.CombineMeshes() 메소드를 이용.

 

Stanadard Assets 메시 에셋 임포트 하고 새 씬. 드로우콜 요인 제거.

CombineMeshesSample 스크립트를 만들고 내용 채우기.

 

https://docs.unity3d.com/ScriptReference/Mesh.CombineMeshes.html

 

void Start() { 에서

.mesh.CombineMeshes( combine );

 

샘플 코드

https://github.com/ozlael/CombineMeshSample

 

05 2D 스프라이트 배칭(Sprite Batching)

3D 오브젝트뿐 아니라 2D 스프라이트도 배칭. 2D 스프라이트는 버텍스가 적어서 배칭이 훨씬 효율적. Static이나 Dynamic 설정이 안돼있어도 자동으로 배칭. 로그라이크 셈플 프로젝트는 2개 배치로 렌더링.

 

스프라이트를 아틀라싱 해서 하나의 이미지만 참조하도록.

 

스프라이트 아틀라스(Sprite Atlas)

2D Sprites pack 에셋. Edit > Project Settings > Editor 에서 Sprite Packer를 Always Enabled로 설정.

Lagacy Sprite Packer 는 예전 Sprite Packing 시스템, 무시.

 

Sprite Packer 를 활성화. Create > Sprite Atlas 선택, 아틀라스 에셋 생성. Inspector 를 확인하면 별도 스프라이트처럼 설정 가능. 하단의 Objects for Packing 에 스프라이트들 드래그앤드롭. Pack Preview 누르면 아틀라스 확인 가능.

 

샘플 코드

https://github.com/ozlael/SpriteAtlasSample

 

06 GPU 인스턴싱(Instancing)

배칭과 달리 동일한 메시의 복사본들을 만든다는 점에서 구분. 배칭보다 런타임 오버헤드가 적다.

CPU에서 처리해서 보내준 정보를 GPU에서 별도의 버퍼에 담고 인스턴싱 처리를 함.

 

샘플 코드

https://github.com/ozlael/GpuInstancingSample

 

사용법, 스탠다드 쉐이더 사용하는 머티리얼의 파라미터 하단에 Enable GPU Instancing 플래그 체크.

유니티가 자동으로 인스턴싱. 만능은 아니고 제약이 있음.

동일한 메시끼리만 한 번의 드로우콜로 처리 가능. 운석들이 분포되어 있는 경우같이, 동일한 모양의 오브젝트들이 여러 개 렌더링 되어야 할 때 유용. Mesh Renderer 사용 메시만 적용. Skinning Mesh 는 적용할 수 없어서 캐릭터는 불가.

 

GPU에서 처리하기 때문에 GPU스펙에 의존적. 디바이스가 지원해줘야 함. 모바일 기기는 OpenGL ES3 이상, Vulkan, Metal 만 가능.

 

4-3 프레임 디버거 (Frame Debugger)

드로우콜 확인은Stat 창이나 Profiler 에서 확인.

구체적으로 무엇이 어떻게 그려지고 배칭이 되고 어떤 것이 배칭이 깨지는지에 대한 상세 부분은 프레임 디버거(Frame Debugger)로 확인.

 

프레임 디버거 = 프레임이 어떻게 렌더링되는지 직관적으로 확인 가능. 각 드로우콜 과정에서 어떤 메시가 렌더링 되는지, 쉐이더의 속성, 배칭처리 중에 드로우콜이 아뉜다면 어떤 이유로 나뉘는지 등.

 

프레임 디버거는 드로우콜 최적화를 위한 필수 툴.

 

Window > Analysis > Frame Debugger

좌측의 Draw 커맨드를 클릭하면 그동안 쌓인 렌더링 결과가 Game View 에 표시됨.

 

배칭으로 처리되지 않았다면 왜 Batched 되지 않았는지 이유도 표시해줌.

MaterialPropertyBlock 이 이전 드로우 오브젝트와 다르다고.

odd negative scaling 은 X 스케일이 좌우 반전을 위해 -1 인 경우 지원되지 않음.

샘플 프로젝트

Endless Runner - Sample Game

Tanks! Tutorial = 스태틱 배칭 렌더링 확인할 수 있음.

 

4-4 컬링 (Culling)

렌더링할 오브젝트 수를 줄이는게 중요. 눈에 보이는 것만 렌더링 할 수 있도록 컬링.

컬링 과정이 효율적일수록 성능 절약됨.

 

01 프러스텀 컬링 (Frustum Culling)

https://docs.unity3d.com/Manual/UnderstandingFrustum.html

기본적인 절두체 컬링.

 

프러스텀 컬링은 유니티 자동 수행. Far Clipping Plane 의 거리가 중요하다.

Fog 를 이용해서 원거리에 잘려나가는 픽셀들을 자연스럽게 처리해서 가려준다.

 

라이팅 윈도우 하단에 Fog 설정. 모바일에선 Fog 설정은 Linear 가 성능에 좋다.

스카이박스가 존재하는 아웃도어 씬에서는 스카이박스와 어울리는 적절한 포그 색을 적용.

 

그래서 모바일에선 프리룩(Free Look) 보다는 탑뷰나 쿼터뷰 카메라 사용.

 

02 오클루전 컬링 (Occlusion Culling)

인도어 씬에서 오클루전 컬링. 다른 오브젝트에 의해 숨겨진 오브젝트 걸러내기. 벽 뒤의 오브젝트 등.

 

오브젝트의 Static 체크박스, 오클루더(Occluder Static)와 오클루디(Occludee Static) 타입.

오클루더는 가리는 오브젝트

오클루디는 가려지는 오브젝트.

보통의 정적 오브젝트는 모든 Static 옵션이 적용되므로 일일이 선택하지 않고 체크한다.

 

오클루전 컬링도 사전에 데이터를 구워놔야 하기 때문에 Static 오브젝트만 가능.

씬 뷰의 Occlusion Culling 을 Edit 로 변경하면 컬링 처리하는 일정 간격의 셀(Cell)로 이루어져 있는 것을 확인 가능.

 

단, 오클루디(Occludee : 가려지는 오브젝트)는 다이나믹 오브젝트도 설정 가능. 캐릭터 등에 적용될 수 있다. Mesh Renderer 의 속성에서 Dynamic Occluded 체크.

 

스테틱 오브젝트(오클루더)에 정적, 동적 오브젝트 모두 가려짐. 오클루전 컬링 윈도우의 Bake 탭에서 상세 파라미터 조절 가능. Smallest Occluder 로 오클루전 컬링용 셀 크기를 조절할 수 있음. 값이 작을수록 오클루전 컬링 정밀도가 올라감. 단, 베이크 하는 데이터 크기가 늘어나고 컬링 연산도 추가 CPU 연산.

 

적절한 파라미터 수치를 찾아야함. Magic Number. 가려지는 오브젝트가 많은 경우에만 활용하는 것이 좋음.

 

03 LOD (Level of Detail)

https://docs/unity3d.com/Manual/class-LODGroup.html

카메라로부터 멀리 떨어져 화면내 차지 비율이 낮으면 디테일 떨굼.

 

LOD 그룹(LOD Group) 컴포넌트 필요. LOD 메시 적용 여부에 따라 LOD1, LOD2 단계 제거 가능.

가로 바는 화면상 차지하는 영역. 하단의 Enderers 에 오브젝트의 Mesh Renderer 컴포넌트 끌어다 놓으면 등록.

아웃도어씬이라면 LOD 로 최적화.

.

유니티 그래픽스 최적화 스타트업 : coupa.ng/bOkEbG

 

유니티 에반젤리스트의 유니티 그래픽스 최적화 스타트업

COUPANG

www.coupang.com

 

 

MSI 지포스 GTX 1660 SUPER 벤투스 S OC 그래픽카드 D6 6GB

COUPANG

www.coupang.com

.

728x90
반응형