예제 - 쿼드 생성
드로우 콜 배칭(Draw Call Batching)

그래픽스 퍼포먼스 최적화(Optimizing graphics performance)

훌륭한 퍼포먼스는 많은 게임의 성공을 결정짓는 중요한 요소입니다. 게임의 렌더링 속도를 최대로 끌어올리는 간단한 가이드라인을 아래에 소개합니다.

높은 그래픽스 효과 찾기

게임의 그래픽 부분은 컴퓨터의 GPU와 CPU 시스템에 주로 영향을 줍니다. 어떤 최적화든 첫 번째로 어디에서 퍼포먼스 문제가 발생하는지 찾아야 합니다. GPU 최적화와 CPU 최적화는 전략이 매우 다르기 때문입니다(아예 정반대라고도 할 수 있습니다. 예를 들어, CPU 최적화를 위해 GPU의 작업을 늘리거나 그 반대인 경우가 흔히 발생합니다).

일반적인 병목현상과 이를 체크하는 방법:

  • GPU가 종종 필레이트 또는 메모리 대역폭의 제한을 받습니다.
    • 디스플레이 해상도를 낮춘 다음 게임을 실행합니다. 낮은 디스플레이 해상도가 게임이 더 빠르게 실행되게 한다면 GPU의 필레이트에 의해 제한되었을 수 있습니다.
  • CPU는 종종 렌더링이 필요할 때 배치의 수에 제한을 받습니다.
    • 렌더링 통계에서 “배치”를 체크합니다. 배치가 더 많이 렌더될 수록 CPU 코스트는 더 높아집니다.

일반적이지 않은 병목현상:

  • GPU에 처리할 버텍스가 너무 많습니다. 훌륭한 성능 보장을 위해 허용되는 버텍스의 수는 GPU와 버텍스 셰이더의 복잡도에 달려있습니다. 일반적으로 모바일에서는 100,000 버텍스를 넘지 않도록 해야 합니다. PC는 몇백만 버텍스도 잘 관리하지만, 최적화 과정에서는 숫자를 최대한 낮추는 것이 좋습니다.
  • CPU가 처리해야 할 버텍스가 너무 많습니다. 스킨드 메시, 천 시뮬레이션, 파티클 또는 다른 게임 오브젝트와 메시일 수 있습니다. 위와 마찬가지로, 게임 품질에 영향을 주지 않는 선에서 숫자를 최대한 낮게 유지하는 것이 좋습니다. 자세한 방법은 아래 CPU 최적화 섹션을 참조하십시오.
  • 렌더링이 GPU 또는 CPU의 문제가 아니라면 다른 곳에 문제가 있을 수도 있습니다. 예를 들어, 스크립트 또는 물리적인 요소입니다. Unity 프로파일러를 사용하여 문제를 찾아야 합니다.

CPU 최적화

스크린에 오브젝트를 렌더링하려면 CPU가 많은 작업을 처리해야 합니다. 오브젝트에 어느 광원이 영향을 주는지, 셰이더와 셰이더 파라미터 설정, 드로잉 커맨드를 그래픽 드라이버에 전송하여 커맨드가 그래픽 카드에 전송되도록 준비하는 등의 작업이 있습니다.

이러한 모든 “오브젝트 별” CPU 사용은 리소스를 많이 사용하는 데 보이는 오브젝트가 많다면 무리가 갈 수 있습니다. 예를 들어 천 개의 삼각형이 있다면 하나의 삼각형을 하나의 메시에 넣는 것(총 1,000개의 메시) 보다는 모든 삼각형을 하나의 메시에 넣는 것이 훨씬 쉽습니다. 두 시나리오 모두 GPU 코스트는 비슷하지만 (한 개 대신)천 개의 오브젝트를 렌더링하기위한 CPU 사용량은 훨씬 높습니다.

보이는 오브젝트 카운트를 줄입니다. CPU의 작업량을 줄이는 방법:

  • 가까이에 있는 오브젝트를 수동이나 Unity의 드로우 콜 배칭을 활용해 결합합니다.
  • 큰 텍스처 아틀라스에 개별적 텍스처를 넣는 등의 방법으로 오브젝트의 머티리얼 수를 줄입니다.
  • 오브젝트가 여러 번 렌더링되는 요소(반사, 그림자, 픽셀 별 광원 등)를 덜 사용합니다.

오브젝트를 합쳐 각 메시가 최소한 수백 개 삼각형을 갖고 전체 메시에 단 하나의 Material 을 갖게 합니다. 머티리얼을 공유하지 않는 오브젝트 두 개를 결합하는 것이 퍼포먼스 향상으로 이어지지 않음을 유의해야 합니다. 여러 머티리얼을 요구하는 일반적인 이유는 두 메시의 텍스처가 같지 않기 때문입니다. 따라서 CPU 퍼포먼스를 최적화하려면 결합하는 모든 오브젝트가 동일한 텍스처를 갖게 해야 합니다

포워드 렌더링 경로에서 픽셀 광원을 많이 사용하면 오브젝트 결합이 합리적이지 않을 수도 있습니다. 이것을 관리하는 것에 관해 더 배우려면 아래 라이팅 퍼포먼스 섹션을 참조하십시오.

GPU: 모델 지오메트리 최적화

모델의 지오메트리를 최적화할 때 두 가지 기본 규칙이 있습니다.

  • 필요 이상으로 삼각형을 사용하지 않습니다.
  • UV 매핑의 경계 부분과 하드 에지(버텍스가 두 배)의 수를 가능한 적게 유지합니다.

그래픽스 하드웨어가 처리해야 할 실제 버텍스의 숫자는 3D 응용 프로그램에서 알려주는 숫자와 보통 일치하지 않습니다. 모델링 응용 프로그램은 일반적으로 지오메트리 버텍스 수, 즉 모델을 구성하는 각 모서리 지점의 수를 나타냅니다. 하지만 그래픽 카드에서 일부 지오메트리 버텍스는 렌더링 목적으로 둘 또는 그 이상의 논리 버텍스로 나눌 필요가 있습니다. 버텍스는 다수의 노멀, UV 좌표나 버텍스 컬러가 있다면 분리돼야 합니다. 결국 Unity에서 버텍스 숫자는 보통 3D 응용 프로그램에서 계수하는 것보다 클 수 밖에 없습니다.

모델의 지오메트리의 수가 GPU에 가장 중요하지만 Unity의 일부 기능은 CPU에서 모델을 처리합니다(예를 들어 메시 스키닝).

조명(Lighting) 퍼포먼스

가장 빠른 방법으로 계산이 전혀 필요없는 라이팅을 생성합니다. 프레임마다 계산하는 대신 라이트매핑으로 정적 조명을 한 번만 “베이크” 해야 합니다. 라이트매핑된 환경을 생성하는 과정은 Unity의 씬에서 라이트를 배치할 때보다 시간이 약간 더 걸릴 뿐입니다.하지만:

  • 실행 속도가 훨씬 빠릅니다(2 픽셀당 광원에서 23 배 빠름).
  • 전역 조명을 베이크하고 라이트 매퍼가 결과를 매끄럽게 하기 때문에 외형이 훨씬 개선됩니다.

많은 경우에 다수의 추가 광원을 추가하는 대신 단순한 트릭을 적용할 수 있습니다. 예를 들어, Rim Lighting 효과를 주기위해 카메라에 바로 광원을 비추는 광원을 추가하는 대신 셰이더에 직접 전용 Rim Lighting 계산을 추가합니다(더 배우려면 표면 셰이더 예제를 참조하십시오).

포워드 렌더링의 광원

또한, 포워드 렌더링을 참조하십시오.

픽셀당 동적 조명은 영향을 받는 모든 픽셀마다 렌더링 작업을 크게 증가시켜 오브젝트가 멀티패스로 렌더링되게 합니다. 모바일이나 저가형 PC의 GPU 등 그다지 강력하지 않은 장치에서는 하나의 오브젝트를 비추는 Pixel Light 를 하나 이상 두지 않습니다. 또한 프레임마다 라이팅을 계산하는 대신 라이트맵으로 정적 오브젝트를 비추게 합니다. 버텍스별 동적 조명은 버텍스 변환에 막대한 작업을 추가시킬 수 있으니 여러 광원이 하나의 오브젝트를 비추지 않도록 해야 합니다.

다른 픽셀 광원의 영향을 받는 원거리의 메시 결합을 피해야 합니다. 픽셀 조명을 사용하는 경우 각 메시는 조명하는 픽셀 광원 수만큼 여러번 렌더링이 필요합니다. 아주 멀리 떨어져 있는 메시 둘을 결합하면 결합된 오브젝트의 효과적인 크기를 증가시킵니다. 결합된 오브젝트의 일부분이라도 비추는 모든 픽셀 조명은 렌더링할 때 고려 대상이므로 필요한 렌더링 패스(pass)의 수가 증가합니다. 일반적으로 결합된 오브젝트의 렌더링에 필요한 패스의 수는 각각의 오브젝트의 패스 수를 더합니다. 그러므로 결합으로 얻는 것이 없습니다.

Unity는 렌더링 중에 메시 주위의 모든 광원을 찾고 어떤 광원이 가장 큰 영향을 미치는지 계산합니다. 품질 설정으로 몇 개의 광원이 각각 픽셀 광원과 버텍스 광원으로 되는지를 조정합니다. 각 광원은 메시에서 떨어진 거리와 조명의 강도를 근거로 각자의 중요성을 계산합니다. 그런데 일부 광원은 게임 맥락에 따라 다른 광원보다 더 중요할 수 있습니다. 그리고 각각의 광원에는 Render Mode 설정이 있어 Important 또는 Not Important 를 정할 수 있습니다. Not Important 로 체크된 광원은 일반적으로 렌더링 오버헤드가 더 낮습니다.

예제: 자동차 경주 게임에서 플레이어의 차량이 헤드라이트를 켜고 어둠 속을 달리는 경우를 보겠습니다. 헤드라이트는 게임에서 가장 중요한 광원이므로 Render ModeImportant 로 설정되어야 합니다. 반면 게임에는 다른 차량의 백라이트 또는 먼 거리의 가로등과 같은, 다소 중요성이 떨어지고 픽셀 광원에 의해 시각적 효과가 개선되지 않는 기타 광원도 있습니다. 그런 광원의 Render ModeNot Important 로 설정돼 효과가 약한 곳에서 렌더링 용량의 낭비를 피합니다.

픽셀별 조명의 최적화는 CPU와 GPU의 작업을 모두 절약합니다. CPU는 처리할 드로우콜이 줄어들고 GPU도 처리할 버텍스가 줄고 추가 오브젝트 렌더를 래스터라이즈할 픽셀 수가 감소합니다.

GPU: 텍스처(Texture) 압축과 밉맵

압축 텍스처를 사용하여 텍스처의 크기를 줄이고 이에 따라 로딩 시간과 메모리 사용을 줄이며 렌더링 퍼포먼스를 비약적으로 향상시킬 수 있습니다. 압축 텍스처는 비압축 32비트 RGBA 텍스처에 필요한 메모리 대역폭과 비교해보았을 때 일부분을 사용하는 정도에 그칩니다.

텍스처(Texture) 밉맵

3D 씬에서 사용되는 텍스처에 항상 밉맵 생성을 활성화합니다. 텍스처 압축이 GPU의 렌더링시 전송되는 텍스처 데이터의 양을 제한하는 데 도움이 되듯 밉맵된 텍스처는 GPU가 작은 삼각형에 낮은 해상도의 텍스처를 사용하게 합니다.

규칙에서 유일한 예외는 텍셀(텍스처 픽셀)이 UI 엘리먼트나 2D 게임에서처럼 렌더링된 화면 픽셀에 1:1로 매핑하는 경우입니다.

LOD 및 레이어별 컬링 거리

컬링 오브젝트에는 오브젝트를 보이지 않게 만드는 작업이 포함됩니다. CPU와 GPU 로드 모두를 효과적으로 줄일 수 있는 방법입니다.

많은 게임에서 플레이어의 게임 경험에 영향을 주지 않고 이렇게 하는 쉽고 효과적인 방법은 작은 오브젝트를 큰 오브젝트보다 보다 적극적으로 컬링합니다. 예를 들어 커다란 건물은 아직 보이는 상태에서 멀리 있는 작은 바위와 파편은 안 보이도록 만들 수 있습니다.

이것을 성취할 방법은 여러가지가 있습니다.

  • LOD 시스템을 사용합니다.

  • 카메라의 레이어별 컬링 거리를 수동으로 설정합니다.

  • 작은 오브젝트를 별개의 레이어에 넣고 레이어별 컬링 거리를 Camera.layerCullDistances 스크립트 함수를 사용하여 설정합니다.

실시간 섀도우

실시간 섀도우가 좋은 면도 있으나 CPU의 추가 드로우 콜과 GPU의 추가 프로세싱을 일으켜 퍼포먼스에 상당한 영향을 끼칩니다. 자세한 내용은 광원 퍼포먼스 페이지를 참조하십시오.

GPU: 고성능 셰이더 작성 팁

플랫폼마다 퍼포먼스의 편차는 큽니다. 고사양 PC GPU는 저가형 모바일 GPU보다 훨씬 더 많은 그래픽스 및 셰이더를 처리할 수 있습니다. 이는 단일 플랫폼에서도 똑같습니다. 빠른 GPU는 느린 내장 GPU보다 몇십 배 빠릅니다.

모바일 플랫폼과 저가형 PC에서 GPU 퍼포먼스는 개발에 사용한 컴퓨터에 비해 훨씬 느립니다. 저가형 GPU 컴퓨터에서 두루 좋은 퍼포먼스를 내기 위해 계산을 줄이고 텍스처 로딩을 줄이려면 수동으로 셰이더를 최적화를 하도록 권장합니다. 예를 들어 Unity의 일부 빌트인 셰이더의 “모바일” 버전은 훨씬 빠르지만 일부 제약 사항이 있고 정확도가 떨어집니다.

모바일 및 저가형 PC 그래픽 카드를 위한 가이드라인을 아래에서 소개합니다.

복잡한 수학적 연산

초월 함수(pow, exp, log, cos, sin, tan 등)는 리소스를 꽤 많이 사용하므로 가능한 경우 사용하지 않아야 합니다. 해당되는 경우 복잡한 수학 연산 대신 룩업 텍스처를 사용하는 방법을 고려하십시오.

연산을 직접 작성하는 것(normalize, dot, inversesqrt 등)을 피해야 합니다. Unity의 빌트인 옵션은 드라이버가 훨씬 나은 코드를 생성할 것을 보장합니다. Alpha Test(discard) 연산은 종종 사용자의 프래그먼트 셰이더를 느려지게 할 수 있음을 기억해야 합니다.

플로팅 포인트 정밀도

플로팅 포인트 변수의 정밀도(float vs half vs fixed)는 대개 데스트톱 GPU에서는 무시되지만 모바일 GPU에서 좋은 퍼포먼스를 내는 데는 중요합니다. 자세한 내용은 셰이더 데이터 타입 및 정밀도 페이지를 참조하십시오.

셰이더 퍼포먼스에 대한 자세한 내용은 셰이더 퍼포먼스 페이지를 참조하십시오.

게임을 더 빠르게 만드는 간단한 체크리스트

  • PC용으로 빌드할 때(타겟 GPU에 따라) 버텍스 수를 프레임당 200K 및 3M 아래로 유지합니다.
  • 빌트인 셰이더를 사용하는 경우 Mobile 이나 Unlit 의 카테고리에서 선택합니다. 비모바일 플랫폼에서도 작동은 하지만 복잡한 셰이더를 단순화시키고 비슷하게 만듭니다.
  • 씬 별로 서로 다른 머티리얼의 수를 적게 유지하고 다른 오브젝트 간에 최대한 많은 머티리얼을 공유합니다.
  • 움직이지 않는 오브젝트에는 Static 프로퍼티를 설정하여, 정적 배칭과 같은 내부 최적화가 가능하도록 해야 합니다.
  • 다수 보다는 단일(가능하다면 방향성) pixel light 만 지오메트리에 영향을 주도록 해야 합니다.
  • 동적 조명을 사용하기보다 조명을 베이크해야 합니다.
  • 가능하면 압축 텍스처 사용하고 32비트보다는 16비트를 사용해야 합니다.
  • 가능하면 안개의 사용을 피해야 합니다.
  • 오클루전 컬링을 사용하여 오클루전이 많아 복잡한 정적 씬에서 드로우 콜과 가시적 지오메트리를 줄이는 데 활용합니다. 오클루전 컬링을 염두에 두고 레벨을 디자인합니다.
  • 스카이박스를 써서 멀리 있는 지오메트리를 “진짜처럼 보이게” 합니다.
  • 멀티 패스 접근 방식 대신 픽셀 셰이더나 텍스처 컴바이너를 활용해 여러 텍스처를 혼합합니다.
  • 가능하면 half 정밀도 변수를 사용합니다.
  • 픽셀 셰이더에서 pow, sin, cos 등 복잡한 수학 연산의 사용을 최소화합니다.
  • 프래그먼트 별 텍스처 사용을 줄입니다.

참고 항목

예제 - 쿼드 생성
드로우 콜 배칭(Draw Call Batching)