유니티 배칭

Like Big·2023년 10월 28일

배칭 설명, 용어 정리

그래픽 처리시 CPU, GPU

  • CPU와 GPU는 서로 다른 작업을 수행하며 CPU에서 그려라는 명령을 GPU에게 전달하는 구조.
  • 드로우콜은 CPU에 부하가 되는 이유는 CPU가 커맨드를 만들어 전달하기 때문이며 자세히 설명하면, CPU는 그래픽스 API를 이용하여 드라이버 칩셋에 알맞은 신호를 전달하여 GPU에 맞는 명령을 해석하고 변형하는 과정도 들어가므로 CPU 오버헤드가 크다.
  • GPU가 busy한 상태에 CPU는 그리라는 명령을 GUP를 기다리지 않고 명령어를 버퍼에 넣어
    전달하며 해당 버퍼를 커맨드 버퍼(Command Buffer)라 하며 메시지 큐(Message Queue)에 의해 전달되며 GPU는 busy하지 않은 상태에서 하나씩 커맨드를 가져간다.
  • 커맨드 버퍼는 각 CPU 스레드마다 버퍼를 만들어 멀티스레드 병렬 처리가 가능하며 공유 메시지 큐에 전달
  • 명령을 바로 전달하면 동기화 문제(관련있는 명령어 모두 받아야 그리기 가능?)가 발생할수 있어 명령을 모아 버퍼 단위로 전송

Render State(렌더 상태)

  • GPU가 렌더링 위해 필요한 정보로 대표적으로 메시, 쉐이더, 텍스쳐, Transform, 각종 옵션-알파블렌딩, ZTest, Stencil가 있다.
  • 렌더링 대상의 정보가 변경될 때 (변경 정보) + (변경 명령) 보내는것을 Render State Change(렌더 상태 변경)
  • CPU가 커맨드를 만드는 것보다 상태 변경에 더 많은 리소스가 소모하는 경우가 있으며,
    CPU는 리소스를 설정하고 GPU에 대한 내부 설정을 변경 작업이 리소스 소모가 가장 큰 작업으로
    핵심적으로 줄여야하는 요소.

Draw Primitive Call (DP Call)

  • CPU가 GPU에 그리라는 명령을 직접적으로 호출하는 것. 가장 저렴한 요청(예 : glDrawElement())
  • 렌더 상태 변경이 없으면 DP Call만 발생.

Draw Call

  • CPU에서 GPU에 렌더링 명령을 전송하는 것을 통칭.
  • 위 설명한 Render State Changes + DP Call를 말함
  • Draw call은 GPU보다 CPU 퍼포먼스에 영향을 미치며 매 프레임 마다 발생하므로 줄이는게 최적화
  • 아래 그림과 같이 드로우콜은 오브젝트 하나하나 그리는 명령을 말하지만, DP Call + Tramsform의 숫자랑 동일한 개수이기도 하다.

Set Pass Call

  • 드로우 콜 시 커맨드 버퍼에서 쉐이더로 인한 렌더 상태 변경만 의미하며 ex)Pass변경, 머테리얼 변경, 쉐이더 파라미터 변경등을 가르킨다.
  • 메시 Trasnform 변경은 렌더 상태 변경이지만 Set Pass Call 에는 포함되지 않음.배칭 기술을 설명하는 부분의 중요 개념

Batch

  • Draw Call와 비슷한 개념이며 드로우콜이 많은 비용을 포함하는 포괄적 개념으로 유니티에서는 배치(DP Call + 메시 Trasnform) + SetPass Call 으로 용어를 나눠 표시를 해줌.
  • 배치를 발생시키는 컴포넌트는 sprite, mesh, line, trail 랜더러가 있는데, paticle system 까지 포함이 된다.

Batching

  • batch를 하나로 묶는 최적화 기법
  • batching을 하여 최적하는 이유는 SetPass Call, DP Call을 줄여 비용 절감

유니티 기본 제공 배칭

  • Static Batching
  • Dynamic Batching
  • GPU Instancing
  • SRP(스크립터블 렌더 파이프라인) Batcher (URP 지원)

다양한 배칭 설명

Draw Call Batching

  • 아래 Static, Dynamic Batching 모두 유니티에서는 드로우 콜 배칭이라 부름

호환성

  • renderpipe_adjust_drawcall_batch
  • mesh, trail, line, sprite 렌더러, particle system 을 지원.
  • skinned 렌더러 및 다른 렌더링 컴포넌트는 지원하지 않음.

기타

  • 동일 머테리얼 사용하는 오브젝트를 배칭.
  • 2D에서 아틀라스?? 자동으로 배칭?
  • MaterialPropertyBlock 사용하여 배칭중단 없이 머티리얼 프로퍼티 변경 가능
    MaterialPropertyBlock??
  • 투명 쉐이더는 그릴때 뒤에있는걸 먼저 그리고 앞에 있는걸 그려야하는 순서로 메시를 뒤-> 앞으로 정렬후 배칭을 시도 하므로 블투명 메시만큼 많은 수의 메시를 배칭할 수 없는 제약이 발생

Static Batching

  • project settings -> player -> Other settings 에 배칭 이미 기본켜져있고 오브젝트에 Batching Static 으로 설정하면 스태틱 배칭이 된다. 정적오브젝트므로 트랜스폼 불가
  • 여러개 메시를 하나의 메시로 통합하여 그리므로 하나만 그리는 Batch가 발생
  • 다행히 컬링은 합치기전에 이루어지므로 메시 기준으로 오픈월드아닌 폐쇄 공간에서 효과적임

    참고 : 다른 배칭 방법으로 메시 수동으로 결합하여 스태틱 배칭처럼 구현하는 방법을 수동배치라하며 이건 컬링이 안됨. 수동 배치 방법 : Mesh.CombineMeshes 통해 또는 미리 에셋에서 결합하여 구현

  • 제한사항 - 여러 매시들의 정점을 모아서 하나로 메시를 만들므로 메모리가 많이 사용됨.

    경고 : C# 스크립트에서 공유 머티리얼 프로퍼티에 액세스할 때 Renderer.material이 아닌 Renderer.sharedMaterial을 사용해야 합니다. Renderer.material은 머티리얼 사본을 만들고 사본을 렌더러에 다시 할당합니경고: C# 스크립트에서 공유 머티리얼 프로퍼티에 액세스할 때 Renderer.material이 아닌 Renderer.sharedMaterial을 사용해야 합니다. Renderer.material은 머티리얼 사본을 만들고 사본을 렌더러에 다시 할당합니다. 이렇게 하면 Unity가 해당 렌더러에 대한 드로우 콜을 배칭하지 않습니다.다. 이렇게 하면 Unity가 해당 렌더러에 대한 드로우 콜을 배칭하지 않습니다. (동일 머테리얼을 사용해야하기 때문)

Dynamic Batching

  • preference -> Core render pipe line -> All visible 설정하고, Project settings 에서 Quality 에서 render pipeline asset을 선택시 사용하고 있는 pipe line을 확인할 수 있다. Show addtional Properties 가 켜져있으면 Dynamic batching이 활성화 되어있는지 확인 가능하다.
  • static 체크하지 않더라도 유니티 내부에서 수행하며 동일한 설정(??)의 버텍스를 그룹화하고 버텍스 버퍼로 넣어 렌더링하여 줄이는 기법이다.
  • 제한사항 skinned 메시 미지원, 버텍스 225 개 이상 메시 미지원, 정점속성 900이상을 가져도 미지원하는데 매번 버텍스 그룹핑으로 CPU리소스 많이 사용되어 제한사항이 발생하는 듯
  • 프레임 디버거에서 Draw Dynamic 이라고 표시된다.
  • 2D 스프라이트 렌더링은 기본적으로 동적 배칭 대상이다.

GPU Instancing

  • GPU에 배칭을 맡기는 방법으로 내부적으로 Graphics.DrawMeshInstanced 호출하여 배칭하는데 게임 오브젝트 사용을 우회하고 지정된 파라미터를 사용하여 화면에 메시를 직접 드로우. 메시 하나만, 트랜스폼 정보만을 전달하여 메모리에 넣고 GPU가 그리는것이다.

  • 머테리얼 -> advanced options 에서 GPU 인스턴싱 옵션을 on

  • 이 방법은 SRP Batcher 상호 호환되지 않는다. 그래서 SRP 배쳐가 된다면 GPU 인스턴싱을 사용하지 못해 SRP Batcher를 꺼야되는데, 그러지 않고 굳이 쉐이더가 SRP 를 지원하지 않으면 GPU 인스턴싱이 되므로 GPU 인스턴싱만 쓰는게 아니라면 굳이 끄지 않아도 된다.

  • 배칭이 적용됨에도 불구하고 MaterialPropertyBlock을 통해서 머테리얼마다 파라미터를 변경 가능(이해, 추가 설명 필요)

  • 제한사항 - 동일 메시, 동일 머테리얼의 경우 배칭, 기본적 shader중 지원하는것 있음

  • WebGL 1.0 제외한 모든 플랫폼 호환되며 Standard, Surface 쉐이더 지원함. SkinnedMeshRenderers 지원 하지 않음.

  • 엄청나게 많은 오브젝트가 모두 하나의 배치로 되는건 아니고 어느정도 그룹화 되어 일정 개수가 넘어가면 페이징 처리되어 배치가 1개 더 늘어난다.

  • 프레임 디버거에서는 RenderLoop Draw라 표기되며 아래 Draw Instanced calls라고 표기가 별도로 된다.(즉 GPU Instacing으로 한 번 그렸다는말)

  • 커스텀 쉐이더에 GPU Instacning 지원에 필요한 셰이더 키워드, 변수 및 함수가 있으며
    GPU 인스턴싱을 지원하는 셰이더 생성 참조

  • 하나의 메시에 256개 미만의 버택스를 가질경우 퍼포먼스가 더 안 좋을 수 있는 경우도 있음 아래 공식 가이드를 참고

    버텍스 수가 적은 메시는 GPU 인스턴싱을 사용하여 효율적으로 처리할 수 없습니다. GPU가 GPU 리소스를 완전히 사용하는 방식으로 작업을 배포할 수 없기 때문입니다. 이러한 비효율적인 처리는 퍼포먼스에 부정적인 영향을 미칠 수 있습니다. 비효율성이 시작되는 임계값은 GPU에 따라 다르지만 일반적으로 256개 미만의 버텍스를 가진 메시에는 GPU 인스턴싱을 사용하지 않습니다.
    버텍스 수가 적은 메시를 여러 번 렌더링하려는 경우 모든 메시 정보가 포함된 단일 버퍼를 생성하여 메시를 드로우하는 것이 가장 좋습니다.

SRP Batcher (URP)

  • 참고 : 프로젝트에서 스크립터블 렌더 파이프라인을 사용하는 경우 MaterialPropertyBlock은 머티리얼에 대한 SRP 배처 호환성을 제거하므로 사용하지 않습니다.
  • 같은 쉐이더만 사용할 때 가능. 다른 머테리얼이라도 Set pass call을 줄일수 있음.
    그러나 배치는 줄일수 없다.
  • 호환성 : built-in 렌더 파이프라인 지원하지 않음. 당연히 URP 기능이기 때문에
  • 쉐이더 선택해서 SRP Batcher : compatible(지원됨)이 되는지 확인하기

Advanced Batching

Batch Renderer Group(BRG)

  • 렌더링을 개발자가 직접 제어. 실제 오브젝트를 만들지 않는다. GPU 버퍼를 직접만들어서 접근한다. 아예 고정된 GPU 메모리 영역을 만들어서 Graphics.DrawMeshInstanced CPU쪽 가비지가 발생할 확률이 적다. 하지만 직접 만들어 할당하므로 실수할 확률이 있음
  • 배치를 최적화 하는것은 유니티가 기본적으로 해주는게 아니므로 사용자 직접 해야하며 배치크기든가 뭔가 정해야할 부분이 있음
  • OpenGLES 3.0에서도 돌아감
  • 컬링을 직접 제어해야함. 게임오브젝트를 유니티에서 제어하지 않기 떄문
  • 스탠다드 SRP 쉐이더도 지원
  • Shader Storage Buffer Object SSBO(128MiB), Unity Buffer ObjectUBO(16kiB)
  • SAO 방식의 데이터를 사용
  • https://blog.unity.com/engine-platform/batchrenderergroup-sample-high-frame-rate-on-budget-devices 에서 받을수 있으며 git lfs쓰지 않음. UBO를 써서 GLES도 지원

기타

우선순위

  • 동일 씬에서 여러 배칭을 쓸 수 있지만 게임 오브젝트는 아래 우선순위로 배칭
  • SRP Batcher -> GPU instancing -> Dynamic Batch
  • SRP는 정적 배칭도 같이 지원
  • 정적 배칭 가능하면 정적 배칭을 하고 GPU instancing 비활성화되고 인스펙터에서도 비활성화하라고 경고뜸, 마찬가지로 GPU instancing 사용 가능 경우 동적 배칭을 비활성화 함

사용자 직접

  • 참고로 사용자가 직접 Graphics.DrawMeshInstanced 호출하여 랜더링 가능하며 게임오브젝트를 만들지 않고 그릴수 있다. 다만 Managed memory 사용해 Grabage 발생할 수 있고(예로 list 에 데이터 넣고 매번 보내주는 작업하면서 발생)
profile
개고운(개발,고양이,운동)

0개의 댓글