オクルージョンポータル
カメラリファレンス

動的解像度

動的解像度は、個々のレンダーターゲットを動的にスケーリングし、GPU の負荷を軽減できるカメラ設定です。アプリケーションのフレームレートが減少する場合は、徐々に解像度を下げて、一貫したフレームレートを維持することができます。アプリケーションが GPU にバインドされている結果、フレームレートが低下しそうなことをパフォーマンスデータが示すと、Unity はこのスケーリングを発動します。また、アプリケーションの特に GPU 負担が大きい部分を見越して、スクリプトを使ってスケーリングを制御することで、スケーリングを手動で発生させることもできます。徐々にスケーリングすると、動的解像度はほとんど目立つことはありません。

サポートされているプラットフォーム

Unity supports dynamic resolution on Xbox One, PS4, Nintendo Switch, iOS, macOS and tvOS (Metal only), Android (Vulkan only), Windows Standalone and UWP (DirectX 12 only).

レンダーターゲットへの影響

動的な解像度では、Unity はレンダーターゲットを再割り当てしません。概念的には、Unity はレンダーターゲットをスケールします。ただし、実際には、Unity はエイリアシングを使用し、縮小されたレンダリングターゲットは、元のレンダーターゲットのほんの一部のみを使用します。Unity は、レンダーターゲットを完全な解像度で割り当てます。次に、動的解像度システムは、新しいターゲットを再割り当てする代わりに、元のターゲットの一部を使用してレンダリングターゲットをスケールダウンし、再度バックアップします。

レンダーターゲットのスケーリング

動的な解像度では、レンダーターゲットには DynamicallyScalable フラグがあります。これを使って、このレンダーテクスチャを動的解像度処理の一部としてスケールするかどうかを設定します。カメラには 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 チェックボックスをチェックする必要があります。

マウスをクリックするか、1 本の指で画面を軽くタップすると、高さと幅の解像度がそれぞれ scaleWidthIncrementscaleHeightIncrement 変数の値だけ下がります。2 本の指でタップすると同じ値で解像度が上がります。

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;

    // 動的解像度のアルゴリズムの値。フレームをまたがって持続されます。
    uint m_frameCount = 0;

    const uint kNumFrameTimings = 2;

    double m_gpuFrameTime;
    double m_cpuFrameTime;

    // 初期化に使用します
    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 はフレームごとに 1 回呼び出されます
    void Update()
    {
        float oldWidthScale = m_widthScale;
        float oldHeightScale = m_heightScale;

        // 1 本指のタップは解像度を下げます
        if (Input.GetButtonDown("Fire1"))
        {
            m_heightScale = Mathf.Max(minResolutionHeightScale, m_heightScale - scaleHeightIncrement);
            m_widthScale = Mathf.Max(minResolutionWidthScale, m_widthScale - scaleWidthIncrement);
        }

        // 2 本指のタップは解像度を上げます
        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);
    }

    // 必要に応じて、次のフレーム時間を見積もり、解像度スケールを更新します
    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 Page published

  • 動的解像度に関するドキュメントは 2017.4 で追加

  • Dynamic Resolution support for macOS (Metal only), Windows Standalone and UWP (DirectX 12 only) added in 2019.1

オクルージョンポータル
カメラリファレンス