온디맨드 렌더링을 이용한 모바일 성능 개선

프로젝트를 가장 높은 프레임 속도로 렌더링하는 것이 늘 바람직하지만은 않은 데, 모바일 플랫폼에서는 특히 더 그렇습니다. Unity 개발자는 그동안 Application.targetFrameRate 또는 Vsync Count를 사용해 Unity 렌더링 속도를 억제했습니다. 이러한 접근 방식은 렌더링뿐만 아니라 Unity가 실행하는 모든 부분의 주기에도 영향을 줍니다. 새로운 온디맨드 렌더링 API를 이용하면 플레이어 루프 주기에서 렌더링 주기를 분리할 수 있습니다.
온디맨드 렌더링을 이용하면 플레이어 루프의 나머지 부분을 높은 빈도로 실행하면서도 프레임 렌더링을 건너뛸 수 있습니다. 이는 모바일 환경에서 특히 유용한데, 렌더링을 건너뛰면 애플리케이션이 터치 이벤트에 대한 반응성을 유지하면서도 성능에 미치는 영향은 줄고 전력은 크게 절약할 수 있기 때문입니다.
다음의 경우에 프레임 속도를 낮추는 것을 고려할 수 있습니다.
메뉴(예: 애플리케이션 진입점 또는 일시 중지 메뉴): 메뉴는 비교적 단순한 씬인 경우가 많으므로 최대 속도로 렌더링할 필요가 없습니다. 낮은 프레임 속도로 메뉴를 렌더링하더라도, 렌더링되지 않은 프레임 동안 입력을 수신하므로 소모되는 전력 을 절감하며 CPU 주파수가 스로틀링될 수 있는 수준까지 기기 온도가 상승하는 것을 방지하는 동시에 원활한 UI 인터랙션을 유지합니다.
턴제 게임(예: 체스): 턴제 게임에는 플레이어가 다음 수를 생각하거나 상대방이 움직일 때까지 기다리는 동안에는 활동량이 낮아집니다. 이 시간 동안 프레임 속도를 낮춰 불필요한 전력 사용을 방지하고 배터리 수명을 연장할 수 있습니다.
정적 콘텐츠: 자동차 사용자 인터페이스(UI)와 같이 콘텐츠가 장시간 동안 정적인 애플리케이션에서 프레임 속도를 낮출 수 있습니다.
성능 관리: 어댑티브 퍼포먼스 패키지를 사용하고 있을 때와 같이, 전력 사용량과 기기 발열을 관리하여 배터리 수명을 극대화하고 CPU 스로틀링을 방지하려는 경우, 렌더링 속도를 조정할 수 있습니다.
머신러닝 또는 AI 애플리케이션: CPU의 렌더링 작업 시간을 줄이면 애플리케이션에서 집중해야 하는 대용량 프로세싱에 성능을 조금 더 활용할 수 있습니다.
어디서나 지원됩니다. 온디맨드 렌더링은 Unity 2019.3에서 지원되는 모든 플랫폼(시스템 요구 사항 참조)과 빌트인 렌더 파이프라인, 유니버설 렌더 파이프라인, 고해상도 렌더 파이프라인 등의 렌더링 API에서 사용할 수 있습니다.
온디맨드 렌더링 API는 UnityEngine.Rendering 네임스페이스의 단 세 가지 프로퍼티로 구성되어 있습니다.
OnDemandRendering.renderFrameInterval
가장 중요한 프로퍼티입니다. 이 프로퍼티를 활용하면 Application.targetFrameRate 또는 QualitySettings.vSyncCount의 분배 계수(dividing factor)인 렌더 프레임 간격을 가져오거나 설정하여 새 프레임 속도를 정의할 수 있습니다. 예를 들어, Application.targetFrameRate를 60으로 설정하고 OnDemandRendering.renderFrameInterval을 2로 설정하면, 두 프레임당 한 번씩 렌더링하기 때문에 프레임 속도가 30fps가 됩니다.
OnDemandRendering.effectiveFrameRate
이 프로퍼티를 사용하면 애플리케이션에서 렌더링하게 될 프레임 속도를 추정할 수 있습니다. 추정값은 OnDemandRendering.renderFrameInterval, Application.targetFrameRate, QualitySettings.vSyncCount의 값과 디스플레이 새로고침 속도를 사용하여 결정됩니다. 그러나 이는 추정값에 불과하므로 정확한 값을 보장하지는 않습니다. 스크립트, 물리, 네트워킹 등의 다른 작업에 CPU를 사용 중인 경우 애플리케이션의 렌더링 속도가 느려질 수 있습니다.
OnDemandRendering.willThisFrameRender
이 프로퍼티를 통해 현재 프레임이 화면에 렌더링되는지 여부를 알 수 있습니다. 렌더링되지 않은 프레임을 사용하여 복잡한 수학 연산, 에셋 로딩 또는 프리팹 생성 등 CPU가 집중적으로 사용되는 작업을 추가로 수행할 수 있습니다.
- 프레임이 자주 렌더링되지 않더라도 이벤트가 스크립트에 전송되는 속도는 평소와 같습니다. 따라서 렌더링되지 않은 프레임 동안에도 입력을 수신할 수 있습니다. 입력 지연을 방지하려면 입력하는 동안renderFrameInterval = 1을 호출하여 버튼, 이동 등의 반응성을 유지해야 합니다.
- 다량의 스크립팅, 물리, 애니메이션 등의 작업이 이루어지지만 렌더링하지 않는 상황에서는 온디맨드 렌더링으로 큰 효과를 보지 못할 수 있습니다. 결과물에 끊김이 발생할 가능성이 있으며 CPU 및 전력 사용량 절감 역시 미미한 수준에 그칠 수 있습니다.
다음 예는 메뉴에 온디맨드 렌더링을 사용하여 입력이 발생하지 않을 때 20fps로 렌더링할 수 있는 방법을 보여줍니다.
using UnityEngine;
using UnityEngine.Rendering;
public class Menu : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
QualitySettings.vSyncCount = 0;
Application.targetFrameRate = 60;
// When the Menu starts, set the rendering to target 20fps
OnDemandRendering.renderFrameInterval = 3;
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButton(0) || (Input.touchCount > 0))
{
// If the mouse button or touch detected render at 60 FPS (every frame).
OnDemandRendering.renderFrameInterval = 1;
}
else
{
// If there is no mouse and no touch input then we can go back to 20 FPS (every 3 frames).
OnDemandRendering.renderFrameInterval = 3;
}
}
}

이 예시 프로젝트를 통해 다양한 상황에서 온디맨드 렌더링을 활용하는 방법을 확인할 수 있습니다.
포럼에서 온디맨드 렌더링을 어떻게 활용하고 있는지 알려주세요. Windows, macOS, WebGL, iOS, Android에서 Unity 에디터와 스탠드얼론 플레이어를 대상으로 테스트를 완료했으나, 피드백은 언제든지 환영합니다.