드로우콜

드로우 콜 : CPU가 GPU에게 어떤 것을 그리라고 명령하는 것

1. 먼저 상태가 변경됐는지(그려야 하는 상태 정보가 다른지) 확인해서 상태 변경 명령을 내리고

2. 메시를 그리라고 명령함 == DP Call

- CPU가 커맨드 버퍼에 명령을 쌓아놓고 GPU가 가져가는 방식으로 병렬 작업이 진행됨

- CPU 성능에 의존적

 

배치

배치 : DP Call과 상태 변경들을 합친 넓은 의미의 드로우 콜

 

Set Pass

Set Pass : 드로우콜이 일어날 때 상태 변경의 발생 여부

- 동일한 머티리얼을 사용한다면 메시의 변경은 포함하지 X

- 쉐이더 변경

 

배칭

배칭 : 드로우콜을 줄이는 작업

- DP Call, 상태 변경들을 묶음

 

최적화 우선순위

1. SRP 배처와 Static 배칭
2. GPU 인스턴싱
3. Dynamic 배칭

 

Static 배칭

스태틱 배칭 : 정적인 오브젝트를 위한 배칭 기법

- Project Settings - Player - Other Settings - Static Batching 체크

- 배칭할 오브젝트에 Batching Static 플래그 체크

 

 

Dynamic 배칭

다이나믹 배칭 : 동적으로 움직이는 오브젝트들끼리 배칭 처리를 하는 기법

- 배칭 처리가 런타임상에 이루어지므로 (static이 아닌 오브젝트들의 버텍스를 모아 합침 -> 다이나믹 배칭에 쓰이는 버텍스 버퍼와 인덱스 버퍼에 담음 -> GPU가 그림) 오버헤드가 발생

- Skinned Mesh에는 적용이 불가능

- 900개 이상의 버텍스 속성과 255개 이상의 버텍스를 포함하는 메시에는 적용이 불가능

 

SRP 배칭

SRP 배칭 :  URP, HDRP, SRP에서 사용하는 배칭

- 공통된 쉐이더를 사용하는 한에서 배칭이 이루어짐

- Material이 달라도 됨

- Batching은 줄지 않지만 SetPassCall이 줄어듬

 

GPU 인스턴싱

GPU 인스턴싱 : 단일 드로우 콜에서 동일한 재질의 메시 사본을 여러 개 렌더링 하는 드로우콜 최적화 기법

- 동일한 메시와 머티리얼을 공유해야함

- 다이나믹 배칭에 비해 런타임 오버헤드가 적음

- 별도의 메시를 생성하지 않고 인스턴싱되는 오브젝트들의 트랜스폼 정보를 버퍼에 담고 GPU가 이것과 원본 메시를 가져다가 한 번에 처리하여 렌더링함

 

적용

따로 적용

Static 배칭

Dynamic 배칭

- 씬에 버텍스 수가 너무 많아 프레임 디버거 사진으로 대체

SRP 배칭

GPU 인스턴싱

 

같이 적용

GPU 인스턴싱과 Static 배칭

SRP 배칭과 Static 배칭

 

결론

=> 프로젝트 상황에 맞게 드로우콜 최적화 기법을 사용하면 될 것 같다.

- 예시 프로젝트는 SRP 배칭과 Static 배칭을 합칠 때 최적화가 제일 잘 되었다.

FPS vs Frame Time

FPS : 1초당 몇 프레임을 렌더링 하는지

- FPS = 1000/ms

Frame Time : 한 프레임을 처리하는데 걸리는 시간

- 일반적으로 1/1000단위로 측정하고 ms(밀리 세컨드)로 표기한다.

- ms = 1000 / FPS

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class DisplayFPS : MonoBehaviour
{
    [SerializeField]
    Text text;

    float frames = 0f;
    float timeElap = 0f;
    float frameTime = 0;

    void Start()
    {
        
    }

    void Update()
    {
        frames++;
        timeElap += Time.unscaledDeltaTime;
        // 1초가 넘었을 때 FPS 갱신하고 timeElap 초기화
        // FPS는 1초에 몇 프레임을 그리는가
        // FPS = ms / 1000
        // frameTime은 1프레임 당 몇 초가 걸리는가
        // frameTime = 1000 / FPS
        if (timeElap > 1f)
        {
            frameTime = timeElap / (float)frames;
            timeElap -= 1f;
            UpdateText();
            frames = 0;
        }
    }

    private void UpdateText()
    {
        text.text = string.Format("FPS = {0} frameTime = {1}", frames, frameTime * 1000.0f);
    }
}

 

왜 Frame Time으로 프로파일링 해야하는가?

1. 결과가 아니라 각 렌더링 순간의 지표가 필요

a구간이 1ms, b구간이 2ms, c구간이 10ms 가 걸린다고 하면 c구간을 최적화 해야 좋을 것이다.FPS는 1초의 평균 값이기 때문에 한 프레임을 렌더링 할 때 걸리는 시간인 Frame Time을 중요시 해야한다.

 

2. FPS는 비선형적 지표이다.ex1)오브젝트가 추가 될 때 성능 변화량이 90FPS, 45FPS, 30FPS일 때 비선형적으로 떨어지는 것으로 보이지만 계산해보면 - 1개 : 1000/90.0 = 11.1ms- 2개 : 1000/45.0 = 22.2ms- 3개 : 1000/30.0 = 33.3ms이처럼 선형적으로 떨어지는 것이다.ex2)씬에서 보면 더 큰 차이가 있다.A씬 : 900FPS -> 450FPS900FPS = 1000 / 900 => 1.1ms450FPS = 1000 / 450 => 2.2msB씬 : 60FPS -> 56.5FPS60FPS = 1000 / 60 => 16.6ms56.5FPS = 1000 / 56.5 => 17.7ms

두 씬에서 모두 동일하게 1.1ms의 영향을 주고 있음을 보인다.

프로파일링 할 때는 FPS보다 Frame Time으로 프로파일링을 하는 것이 좋다.

 

데이터를 측정할 때는 여러 번 측정해 평균 값을 데이터로 사용하자.

매번 잴 때마다 값이 조금씩 다를 수 있기 때문이다.

또한 각 시나리오마다 다를 수 있는데 평균적으로 NPC가 몇 없는 마을보다 몬스터가 많이 나오는 사냥터가 FPS가 더 낮게 나오겠지만 NPC의 모델의 텍스처가 매우 크다면 마을이 더 낮을 수도 있다.

ex1)

아래의 Rendering Path가 Deffered로 되어있다면 기본적으로 대역폭에 요구되는 성능 비용이 커서 FPS가 낮게 나올 수 있다.

ex2) 

포스트 프로세싱 등 후처리 효과도 비용이 크다.

 

Target Frame Rate

모바일 디바이스에서 쓰로틀링 상태로 진입하는 상태를 막기 위해 강제로 최고 FPS를 낮추는 방법이다.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TargetFrameRate : MonoBehaviour
{
    void Start()
    {
        Application.targetFrameRate = 40;
    }
}

프로파일링 할 때는 FPS가 60까지 충분히 나오는지 확인하기 위해서 Application.TargetFrameRate를 60으로 설정하는 것이 좋다.

60프레임으로 표현할 수 있을 만큼 성능이 충분함에도 불구하고 40으로 제한을 두는 것은 그만큼 프로세서가 쉴 수 있는 시간을 충분히 확보해 둔다는 의미이다.

 

* Target Device : 갤럭시 노트 8

WaitForTargetFPS는 디바이스가 작업 후 쉴 시간이다. 쓰로틀링 방지를 위해 여유분이 있으면 좋다.

Target Frame = 30으로 설정 시 WaitForTargetFPS 10ms정도로 넉넉히 돌아간다.

Target Frame = 60으로 설정 후 시간이 좀 지났을 때 프로파일링 시  WaitForTargetFPS가 간신히 적용되는 것을 볼 수 있었다.

추가로 모바일 기기에서 TargetFrame = 40; 이 동작하지 않는 경우가 있었는데 이는 60Frames를 integer 값으로 나눌 수 있는 값이어야 한다고 한다.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TargetFrameRate : MonoBehaviour
{
    void Start()
    {
        QualitySettings.vSyncCount = 0;
        Application.targetFrameRate = 40;
    }
}

 

VSync(수직 동기화)

targetFrameRate를 높게 설정해도 프레임이 60FPS를 넘지 않을 때 VSync를 의심해보자.

VSync(Vertical Synchronization = 수직동기화) : 모니터에서 렌더링 할 때 더블 버퍼링을 사용하는데 이 때 모니터의 주파수에 맞게 렌더링 퍼포먼스를 조절하는 것

더블 버퍼링 : 백 버퍼와 프론트 버퍼를 사용해서 다음에 출력될 화면을 백 버퍼에 집어 넣고 화면이 갱신될 때 프론트 버퍼와 백 버퍼를 바꿔치게 해 백 버퍼의 내용을 보여 줌으로써 자연스러운 화면 전환이 일어나게끔 하는 것

티어링 : 더블 버퍼링으로 각 버퍼 간의 전환이 이루어질 때 화면이 섞여 출력되어서 화면이 찢어지는 것처럼 보이는 것

 

=> 성능 및 병목을 측정할 떄는 VSync를 끄는 것이 좋다.

Vsync 끄기 : Edit - Project Settings - Quality - Don't Sync

퀄리티 세팅

런타임 중에 설정된 레벨을 변경하려면 QualitySettings.SetQualityLevel 메소드를 통해 현재 적용되어 있는 레벨을 변경할 수 있다.

 

쓰로틀링 고려하기

- 타깃 디바이스가 얼마나 빠르게 쓰로틀링 상태로 진입하고 어느 정도의 성능 하락이 일어나는지 등을 먼저 체크한다.

- 측정 중인 모바일 디바이스를 노트북이나 데스크톱에서 최대한 멀리 떨어뜨려놓는다.

- 항상 기기가 100% 충전된 상태에서 측정한다.

- 측정 후 다음 측정까지 5분 정도의 쿨타임을 가지고 진행한다.

- 스마트폰 전용 쿨링팬이나 아이스팩으로 쓰로틀링 상태 진입을 억제해 줄 수도 있다.

- 최신 기종일수록 성능이 좋아 쓰로틀링 상태에 더 빨리 진입하고 그 격차가 크다.

- 기기마다 쓰로틀링 단계가 다르다. 성능 하락이 한 단계가 아니라 시간이 지남에 따라 여러 단계로 성능 하락이 일어나기도 한다.

 

참고 :

https://www.yes24.com/Product/Goods/67305196

 

유니티 그래픽스 최적화 스타트업 - 예스24

게임개발의 최대 난적, 그래픽스 최적화를 다루는 책이 책은 게임개발의 최대 난적이라 할 수 있는 게임개발의 최적화에 대해서 다루는 책이다. 특히 유니티 엔진을 기반으로 게임을 가볍게 만

www.yes24.com

https://forum.unity.com/threads/application-targetframerate-not-working.169299/

 

Application.targetFrameRate not working

Hi guys been doing on this issue for hours now, Application.targetFrameRate is not working no matter what I do. I've tried changing it to 20, 25,...

forum.unity.com

 

'유니티 그래픽스 최적화 스타트업' 카테고리의 다른 글

드로우 콜과 배칭  (1) 2023.12.20

+ Recent posts