yhc509

유니티 그래픽스 최적화 스타트업 (2) - DrawCall, Batching, Culling

·11 min read

DrawCall

  • CPU가 GPU에게 렌더링 명령을 내리는 것
    • 명령을 내릴때에는 메시, 텍스쳐, 셰이더, 트랜스폼, 알파 등의 정보가 함께 간다
    • CPU메모리 , GPU메모리간 통신 필요
    • 오버헤드 발생 = CPU 성능에 의존적 = CPU 바운드
  • 렌더상태(Render State)
    • GPU가 그려야 하는 상태 정보를 담는 테이블 (메시, 텍스쳐, 셰이더 등 어떤걸 사용할지…)
    • CPU가 렌더 상태를 변경하는 명령을 보내면 GPU가 렌더상태에 정보를 저장함
    • 렌더상태 명령들을 보낸 후, CPU가 마지막으로 메시를 그리라는 DP Call(Draw Primitive Call)을 보냄.
  • CPU와 GPU는 병렬적으로 작업하기 때문에 명령을 내린다고 해서 바로 수행할 수 없음
    • 커맨드버퍼에 명령들을 쌓아두고 수행이 가능할 때 순차적으로 수행함
    • Vulkan이나 Metal에서는 여러개의 커맨드버퍼로 멀티쓰레드 처리를 함
  • CPU바운드는 주로 메인쓰레드에서 발생함
    • 부하를 줄이기 위해 멀티쓰레디드 렌더링(Multithgreaded Rendering)을 사용할 수 있음
    • Edit → Project Settings → Player의 Multithgreaded Rendering 옵션
    • CPU의 멀티코어를 활용하여 수행하게 됨.
    • 단, 구형 디바이스는 CPU의 멀티코어가 많지 않으므로 효율이 보장되지 않음.
    • iOS는 Metal API가 지원될 때만 활성화됨.
    • WebGL에선 아예 지원되지 않음
    • 드로우콜 병목이 아니라면 멀티쓰레디드 렌더링은 영향이 거의 없음
  • 드로우콜 발생 조건
    • 메시 1 / 머터리얼 1 = 드로우콜 1
    • 메시가 여러개인 경우
      • 한 오브젝트의 메시가 17조각으로 구성되어 있다면 = 드로우콜 17
      • 그 오브젝트가 10개 있다면 = 드로우콜 170
    • 머터리얼이 여러개인 경우
      • 한 오브젝트의 머터리얼이 2개라면 = 드로우콜 2
    • 셰이더에 의한 경우
      • 멀티패스(Multi Pass)로 두번 이상 렌더링을 거치는 셰이더라면 두번의 배치 발생
        • 외곽선(Outline) 셰이더는 보통 원래 메시를 그린 후, 추가로 외곽선을 그리는 2단계
  • 보통 PC환경에선 드로우콜 1000개도 가능
    • 모바일에선 100~200개
  • 유니티에선 드로우콜을 Batch와 SetPass라는 용어로 표현한다
    • Batch : 상태 변경과 DP Call을 합친, 넓은 의미의 드로우콜
    • SetPass : 상태변경 여부
    • Batch가 10번인데 SetPass가 1번이라면?
      • 10번의 드로우콜 동안 셰이더 변경이 없이 이루어졌다.
      • 메시, 트랜스폼 정보 등의 최소한의 상태변경만 이루어졌다.
    • 왠만하면 SetPass = 셰이더에 의한 렌더링 패스 횟수
      • 셰이더나 파라메터가 바뀐 경우 SetPass 증가
      • 서로 다른 메시여도 같은 머터리얼이라면 SetPass는 늘어나지 않음
        • SetPass call이 적으면 Batch 구성이 잘 되어 있다고 볼 수 있음
      • GPU로 명령을 보낼때 SetPass가 많은 비용을 차지함
      • 드로우콜 병목이라면 SetPass를 줄이는 것이 효율이 좋을 수 있음

Batching

  • 여러 배치를 하나의 배치로 묶는 것을 통해 드로우콜을 줄이는 작업.

  • 3개의 오브젝트 = 3번의 드로우콜?

    • 오브젝트가 모두 같은 머터리얼을 쓴다면 3개 메시를 배칭하여 1번의 드로우콜로 표현한다.
  • 같은 머터리얼, 같은 텍스쳐를 사용해야 함.

    • 텍스쳐 아틀라스를 쓰는 이유.
  • 스태틱 배칭과 다이나믹 배칭

    • Edit → Project Settings → Player 옵션
  • 스태틱 배칭(Static Batching)

    • 정적이고 움직이지 않는 오브젝트를 위한 배칭 기법 (배경 오브젝트 등)
    • 게임 오브젝트의 static 플래그가 켜져 있어야 함
      • 이동, 회전, 스케일 조절이 되지 않는 게임오브젝트
      • 로딩 타임에 자동 배칭 처리가 된다.
      • 씬 로딩때 처음부터 존재해야 한다. 게임 전투 배경씬 등이 따로 존재하는 이유.
        • Max나 Maya에서 합쳐서 만드는 것보다 유니티에서 스태틱 배칭을 하는게 낫다. (컬링 이슈)
    • 다이나믹 배칭보다 효율적이다
      • 런타임에 버텍스 연산을 하지 않는다
      • 하지만 메모리가 추가로 필요하다. 여러 오브젝트들의 메시를 합친 새로운 메시를 만들어내기 때문.
      • 메모리가 문제된다면 스태틱배칭을 포기해야 한다.
    • 스태틱 배칭이라 하더라도 라이트맵, 라이트 프로브, 동적 라이팅 등으로 인해 배칭이 나뉠 수는 있다.
  • 다이나믹 배칭(Dynamic Batching)

    • 움직이는 오브젝트들끼리 배칭 처리를 하는 기능

    • 런타임에 버텍스, 인덱스 버퍼를 합쳐주는 작업 = 오버헤드

      • 오버헤드 비용이 드로우콜 비용보다 적으면 이득
    • Skinned Mesh에 적용 불가.

      • 렌더링 전에 스키닝 연산이 일어나면서 버텍스 위치 재계산이 이루어짐
      • 스키닝 연산을 CPU / GPU 누가 할지 설정 가능
        • Edit → Project Settings → Player → Other Settings → GPU Skinning
      • CPU보다 GPU병목이라면 CPU스키닝으로 처리
      • Metal, Vulkan에선 GPU스키닝 불가
    • 버텍스가 너무 많으면 제외됨.

      • 모바일 기준 포지션, 노말, UV를 사용하는 모델이라면 300 버텍스 정도 이하
    • 특정 오브젝트만 다이나믹 배칭 제외할 수 있음

      SubShader {
          Tags { "RenderType"="Opaque" "DisableBatching"="True" }
      
  • Mesh.CombineMeshes()

    • 스태틱, 다이나믹 배칭 외에 수동으로 배칭 처리를 할 수 있음
    • 스크립트로 메시들을 강제로 합쳐줌
    • 런타임 도중에 파츠가 조합되어 오브젝트가 만들어지는 경우 활용
  • 스프라이트 배칭 (Sprite Batching)

    • 하나의 이미지에 여러 스프라이트를 모아서 사용.
    • 스프라이트 아틀라스
  • GPU 인스턴싱

    • 다이나믹 배칭과 스태틱 배칭에 비해 런타임 오버헤드가 적음
    • GPU 인스턴싱은 별도의 메시를 생성하지 않는 대신, 인스턴싱 되는 오브젝트들의 트랜스폼 정보를 별도의 버퍼에 담음.
    • GPU는 트랜스폼 정보가 담긴 버퍼와 원본 메시를 가져다가 한번에 렌더링함 = GPU 연산
    • 스탠다드 셰이더 → Enable GUI Instancing
    • 동일 메시여야 하고, MeshRenderer를 사용해야 함 (SkinnedMeshRenderer는 안됨)
      • 캐릭터는 GPU인스턴싱을 사용할 수 없음!
      • OpenGL ES 3.0 / Vulkan / Metal만 사용 가능

Culling

  • 렌더링이 필요한 오브젝트를 추리는 과정
  • 프러스텀 컬링(Frustum Culling)
    • 카메라를 통해서 시야 밖의 오브젝트를 걸러내는 기능
      • 카메라의 Clipping Planes ⇒ Far , Near
      • 멀리있는 오브젝트가 잘리면 어색함 ⇒ Fog로 숨긴다
        • Windows → Rendering → Lighting Settings
        • 모바일은 Linear 사용 권장
  • 오클루전 컬링(Occlusion Culling)
    • 다른 오브젝트에 의해 숨겨진 오브젝트를 걸러내는 기능
    • Window → Rendering → Occlusion Culling 의 Bake를 하여 오클루전 데이터를 사전에 연산한다
      • Smallest Occluder : 씬을 일정 크기의 셀로 나눔. 셀의 크기. 작을수록 정밀하지만 데이터가 늘어나고 오버헤드 발생
    • Static 정보를 토대로 연산함
      • 오클루더 스태틱 : 다른 오브젝트를 가리는 역할
      • 오클루디 스태틱 : 다른 오브젝트에 의해 가려지는 역할
      • 어지간하면 둘 다 해당함.
    • 다이나믹 오브젝트여도 오클루디가 될 수 있음
      • Mesh Renderer → Dynamic Occluded
    • 외부 씬이라면 가려지는 오브젝트가 많지 않아서 효율이 나오지 않을 수 있음
  • LOD (Level Of Detail)
    • 오브젝트에 LOD Group 컴포넌트 추가
    • 외부 씬에서 효율이 좋을 수 있음

참고 자료