오클루전 컬링
머티리얼, 셰이더, 텍스처

다이내믹 해상도

다이내믹 해상도는 개별 렌더 타겟을 동적으로 스케일링하여 GPU의 부하를 줄여주는 카메라 설정입니다. 애플리케이션의 프레임 속도가 감소하는 경우에는 일관된 프레임 속도를 유지하기 위해 점진적으로 해상도를 스케일다운할 수 있습니다. 성능 데이터가 애플리케이션의 GPU 바운드로 인해 프레임 속도가 떨어진다고 암시하는 경우 Unity는 이 스케일링 방식을 트리거합니다. 애플리케이션의 특정 GPU 과부하 섹션을 선점한 후 스크립트를 통해 스케일링을 제어하는 방식으로 스케일링을 수동으로 트리거할 수도 있습니다. 점진적으로 스케일링하면 다이내믹 해상도는 거의 눈에 띄지 않습니다.

지원되는 플랫폼

Unity는 Xbox One, PS4, Nintendo Switch, iOS/tvOS (Metal 전용), Android (Vulkan 전용)에서 다이내믹 해상도를 지원합니다.

렌더 타겟에 대한 영향

다이내믹 해상도를 사용하는 경우 Unity는 렌더 타겟을 재할당하지 않습니다. 개념적으로는 Unity가 렌더 타겟을 스케일링하지만, 실제로 Unity는 앨리어싱을 사용합니다. 또한 스케일다운된 렌더 타겟은 원본 렌더 타겟의 작은 부분만 사용합니다. Unity가 전체 해상도로 렌더 타겟을 할당하면, 다이내믹 해상도 시스템이 스케일다운을 통해 다시 백업합니다. 이때 새 타겟을 재할당하지 않고 원본 타겟의 일부를 사용합니다.

렌더 타겟 스케일링

다이내믹 해상도를 사용하는 경우 렌더 타겟에 DynamicallyScalable 플래그가 생깁니다. 이 플래그를 설정하면 Unity가 다이내믹 해상도 프로세스의 일부로 이 렌더 텍스처를 스케일링해야 하는지 여부를 지정할 수 있습니다. 또한 카메라에 생기는 allowDynamicResolution 플래그를 사용하여 다이내믹 해상도를 설정하면, 덜 복잡한 씬에 다이내믹 해상도를 적용하는 경우에 렌더 타겟을 오버라이드할 필요가 없습니다.

MRT 버퍼

카메라에서 Allow Dynamic Resolution 을 활성화하면 Unity는 카메라의 모든 타겟을 스케일링합니다.

스케일링 제어

ScalableBufferManager를 통해 스케일을 제어할 수 있습니다. ScalableBufferManager를 이용하면 다이내믹 해상도 시스템에서 스케일링하도록 표시한 모든 렌더 타겟의 동적 너비와 높이 스케일을 제어할 수 있습니다.

예를 들어 애플리케이션이 원하는 프레임 속도로 실행되지만, 일부 환경에서 증가된 파티클, 포스트 효과, 화면 복잡도 등으로 인해 GPU 성능이 저하된다고 가정하겠습니다. Unity FrameTimingManager를 이용하면 CPU 또는 GPU 성능이 저하되는 순간을 탐지할 수 있습니다. 따라서 FrameTimingManager를 사용하여 새로운 너비와 높이 스케일을 계산하여 프레임 속도를 원하는 범위 내에서 유지하고 해당 값을 스케일다운하여 즉시 또는 일련의 프레임 구간에서 점진적으로 성능을 안정적으로 유지할 수 있습니다. 화면 복잡도가 감소하고 GPU가 일관되게 동작하면 GPU가 처리할 수 있다고 계산한 원래 값으로 너비와 높이 스케일을 다시 높일 수 있습니다.

예제

이 예시 스크립트는 기본적인 API 사용 사례를 나타냅니다. 이 스크립트를 씬의 카메라에 추가하고 카메라 설정에서 Allow Dynamic Resolution 을 선택하십시오. 또한 Player 설정을 열고(메뉴: Edit > Project Settings 로 이동한 다음 Player 카테고리 선택) Enable Frame Timing Stats 체크박스를 선택해야 합니다.

마우스 오른쪽 버튼을 클릭하거나 화면을 한 손가락으로 탭하면 높이 및 너비 해상도가 scaleWidthIncrementscaleHeightIncrement 변수의 양만큼 각각 감소합니다. 두 손가락으로 탭하면 동일한 인크리먼트만큼 해상도가 증가합니다.

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

public class DynamicResolutionTest : MonoBehaviour
{
    public Text screenText;

    FrameTiming[] frameTimings = new FrameTiming[3];

    public float maxResolutionWidthScale = 1.0f;
    public float maxResolutionHeightScale = 1.0f;
    public float minResolutionWidthScale = 0.5f;
    public float minResolutionHeightScale = 0.5f;
    public float scaleWidthIncrement = 0.1f;
    public float scaleHeightIncrement = 0.1f;

    float m_widthScale = 1.0f;
    float m_heightScale = 1.0f;

    // Variables for dynamic resolution algorithm that persist across frames
    uint m_frameCount = 0;

    const uint kNumFrameTimings = 2;

    double m_gpuFrameTime;
    double m_cpuFrameTime;

    // Use this for initialization
    void Start()
    {
        int rezWidth = (int)Mathf.Ceil(ScalableBufferManager.widthScaleFactor * Screen.currentResolution.width);
        int rezHeight = (int)Mathf.Ceil(ScalableBufferManager.heightScaleFactor * Screen.currentResolution.height);
        screenText.text = string.Format("Scale: {0:F3}x{1:F3}\nResolution: {2}x{3}\n",
            m_widthScale,
            m_heightScale,
            rezWidth,
            rezHeight);
    }

    // Update is called once per frame
    void Update()
    {
        float oldWidthScale = m_widthScale;
        float oldHeightScale = m_heightScale;

        // One finger lowers the resolution
        if (Input.GetButtonDown("Fire1"))
        {
            m_heightScale = Mathf.Max(minResolutionHeightScale, m_heightScale - scaleHeightIncrement);
            m_widthScale = Mathf.Max(minResolutionWidthScale, m_widthScale - scaleWidthIncrement);
        }

        // Two fingers raises the resolution
        if (Input.GetButtonDown("Fire2"))
        {
            m_heightScale = Mathf.Min(maxResolutionHeightScale, m_heightScale + scaleHeightIncrement);
            m_widthScale = Mathf.Min(maxResolutionWidthScale, m_widthScale + scaleWidthIncrement);
        }

        if (m_widthScale != oldWidthScale || m_heightScale != oldHeightScale)
        {
            ScalableBufferManager.ResizeBuffers(m_widthScale, m_heightScale);
        }
        DetermineResolution();
        int rezWidth = (int)Mathf.Ceil(ScalableBufferManager.widthScaleFactor * Screen.currentResolution.width);
        int rezHeight = (int)Mathf.Ceil(ScalableBufferManager.heightScaleFactor * Screen.currentResolution.height);
        screenText.text = string.Format("Scale: {0:F3}x{1:F3}\nResolution: {2}x{3}\nScaleFactor: {4:F3}x{5:F3}\nGPU: {6:F3} CPU: {7:F3}",
            m_widthScale,
            m_heightScale,
            rezWidth,
            rezHeight,
            ScalableBufferManager.widthScaleFactor,
            ScalableBufferManager.heightScaleFactor,
            m_gpuFrameTime,
            m_cpuFrameTime);
    }

    // Estimate the next frame time and update the resolution scale if necessary.
    private void DetermineResolution()
    {
        ++m_frameCount;
        if (m_frameCount <= kNumFrameTimings)
        {
            return;
        }
        FrameTimingManager.CaptureFrameTimings();
        FrameTimingManager.GetLatestTimings(kNumFrameTimings, frameTimings);
        if (frameTimings.Length < kNumFrameTimings)
        {
            Debug.LogFormat("Skipping frame {0}, didn't get enough frame timings.",
                m_frameCount);

            return;
        }

        m_gpuFrameTime = (double)frameTimings[0].gpuFrameTime;
        m_cpuFrameTime = (double)frameTimings[0].cpuFrameTime;
    }
}

참고 항목


  • 2018–09–20 편집 리뷰를 거쳐 페이지 게시됨

  • Unity 2017.4에서 다이내믹 해상도에 대한 설명 추가됨

오클루전 컬링
머티리얼, 셰이더, 텍스처