Version: 2020.2
Streaming Controller
Color space

Mip Map Streaming API

このページは以下について説明します。

概要

Use the API to apply more control over how Unity streams Textures. You can override which mip map level to load for specific Textures, while the Mip Map Streaming system automatically manages all other Textures. You might have specific gameplay scenarios where you know that Unity needs to fully load certain Textures. For example, moving large distances quickly, or using instantaneous Camera cuts, can cause noticeable Texture quality changes while the Mip Map Streaming system streams the mip maps from the disk into memory. To reduce this problem, you can use the API to preload mip maps at a new Camera location .

To enable and control Mip Map Streaming on a Texture, use the following properties:

Mip Map Streaming automatically reduces the size of Textures until they fit into the Mip Map Streaming Memory Budget. The Texture’s Mip Map Priority number is roughly a mip map offset for the Memory Budget. For example, with a priority of 2, the Mip Map Streaming system tries to use a mip map that is two mip levels higher than other Textures with a priority of 0. Negative values are also valid. If it can’t do this, it uses a lower mip level to fit the Memory Budget.

Controlling the Mip Map Streaming system

These properties are read-only at runtime:

To control what happens at runtime, use the following static properties:

To control the way Unity caches unused mip levels via script, use Texture2D.streamingTextureDiscardUnusedMips. It’s useful to set this to true for initial testing to check metrics, and to set a Memory Budget (set in Quality Settings when Texture Streaming is enabled, or via QualitySettings.streamingMipmapsMemoryBudget), otherwise Unity doesn’t drop any mips. The Memory Budget is set to 512MB by default.

Controlling Cameras for Mip Map Streaming

In the Quality Settings (Edit > Project Settings > Quality), use Add All Cameras to specify whether Unity should calculate Mip Map Streaming for all Cameras in the Project. This is enabled by default.

どのカメラをアクティブにするかを細かく制御するには、Camera コンポーネントがアタッチされているゲームオブジェクトの Streaming Controller コンポーネントを使用します。これにより、Camera コンポーネントから位置やカメラ設定 (Field of View など) を直接取得します。

If the Camera is disabled, Unity does not calculate Mip Map Streaming for it, unless the Streaming Controller is enabled and in the preloading state. When the Streaming Controller and associated Camera are enabled, or if the Streaming Controller is in a preloading state, then Unity calculates Mip Map Streaming for this Camera. If the Streaming Controller is disabled, then Unity does not calculate Mip Map Streaming for this Camera.

Streaming Controller コンポーネント
Streaming Controller コンポーネント

Streaming Controller コンポーネントには Mip Map Bias 設定が含まれています。これを API で制御するには、StreamingController.streamingMipmapBias を使用します。

Use this setting to force Unity to load smaller or larger mip map levels than the Mip Map Streaming system has chosen for those Textures. Use the numerical field to set the offset that Unity applies to the mip map level. Unity adds this offset to all Textures visible from this Camera.

カメラカット

When cutting from one location to another, the Mip Map Streaming system needs time to stream the required Textures into Unity. To trigger preloading at a disabled target Camera location, call StreamingController.SetPreloading on the target Camera’s Streaming Controller component. You can specify a time-out to end the preloading phase. To automatically enable the Camera at the end of the preloading phase, set the activateCameraOnTimeout flag to true in script. To disable a Camera after you cut from it to the new one, pass that Camera as the disableCameraCuttingFrom parameter.

void StreamingController.SetPreloading(float timeoutSeconds=0.0f, bool activateCameraOnTimeout=false, Camera disableCameraCuttingFrom=null)

プリロード状態をキャンセルまたはクエリするには、以下のメソッドを使用します。

To determine whether the Mip Map Streaming system is still loading Textures, you can query the following properties:

Note that there is delay between when you enable a Camera and when these properties become a value other than zero. This delay is because the Mip Map Streaming system calculates the mip maps using time-sliced processing. For this reason, during a Camera cut you should wait a minimum length of time before the cut. Texture budget and Scene movement can cause continuous Mip Map Streaming, so you also need to set a maximum length of time before the cut.

Load a specific mip map

特定テクスチャのミップレベルの計算をオーバーライドするには、Texture2D.requestedMipmapLevel を使用します。これは、その特定テクスチャの 0 から最大の範囲内で設定できる正確なミップレベルです。または、それより低い場合は Max Level Reduction 値を使用します。0 が最高解像度ミップレベルです。

要求されたミップレベルがロードされているかどうかを確認するには、Texture2D.IsRequestedMipmapLevelLoaded を使用します。

リクエストしたミップレベルでのオーバーライドを止め、システムにミップマップレベルを計算させたい場合は、Texture2D.ClearRequestedMipmapLevel を使用して値をリセットします。

メッシュの UV 密度の推定値を取得するには、以下を使用します。

float Mesh.GetUVDistributionMetric(int uvSetIndex)

UV 分布メトリックを使用すると、カメラの位置に基づいて、必要なミップマップレベルを計算することができます。コードサンプルは Mesh.GetUVDistributionMetric を参照してください。

システムをオーバーライドしてすべてのミップを強制的にロードするには、Texture.streamingTextureForceLoadAll を使用します。

関連 API メソッド

マテリアルに割り当てられたテクスチャを取得および設定するには、以下を使用します。

マテリアルのすべてのテクスチャプロパティーを取得するには、以下を使用します。

Debugging Mip Map Streaming

Unity has a built-in Mip Map Streaming debugging view mode. To access it, click the Scene view control drop-down and select Texture Streaming. This view mode tints GameObjects the following colours, depending on their status in the Mip Map Streaming system:

  • Green for Textures that have reduced mip maps due to the Mip Map Streaming system.
  • Red for Textures that have fewer mip maps because the Mip Map Streaming system does not have enough resources to load them all.
  • Blue - ストリーミングに設定されていないテクスチャ、またはミップレベルを計算するレンダラーがない場合。

スクリプトを使ったデバッグ

デバッグモードのマテリアルプロパティーをアップロードするには、Texture.SetStreamingTextureMaterialDebugProperties を使用します。

デバッグモードのマテリアルプロパティーをアップロードするには、以下のプロパティーを使用します。

To get information about the number of Textures or renderers the Mip Map Streaming system is interacting with, use the following properties:

ミップマップレベルに関する情報を取得するには、以下のプロパティーを使用します。

Lightmaps

You can use the Mip Map Streaming system to stream lightmaps. You can edit the Texture settings directly, but they reset to their default values when Unity regenerates the lightmaps. The Player Settings (Edit > Project Settings > Player) provide two controls to set streaming and priority for generated lightmaps: Lightmap Streaming Enabled and Streaming Priority.

Edit > Project Settings > Player > Other Settings
Edit > Project Settings > Player > Other Settings

再生モード

Mip Map Streaming is enabled in Play Mode by default. However, when running in Play Mode, the Editor overheads bias the statistics. To get accurate figures, test the application on the target device.

When Mip Map Streaming is enabled in Play mode, but not enabled in Edit mode (or the other way around), toggling in and out of Play mode takes slightly longer. To disable Mip Map Streaming in Play mode, go to the Editor Settings (Edit > Project Settings > Editor), navigate to Streaming Settings and disable Enabled Texture Streaming in Play Mode. This prevents Unity from unloading and reloading mip map data, and should speed up the Play mode workflow.

Edit > Project Settings > Editor > Streaming Settings
Edit > Project Settings > Editor > Streaming Settings

テクスチャストリーミングの状態のデバッグスクリプト

Add the following script to a GameObject in the Scene to display the Mip Map Streaming status. This is useful if you want to determine the correct memory budget to set.

Shader "Show Mip Map Streaming" {
    Properties {
        _MainTex ("", 2D) = "white" {}
        _Control ("Control (RGBA)", 2D) = "red" {}
        _Splat3 ("Layer 3 (A)", 2D) = "white" {}
        _Splat2 ("Layer 2 (B)", 2D) = "white" {}
        _Splat1 ("Layer 1 (G)", 2D) = "white" {}
        _Splat0 ("Layer 0 (R)", 2D) = "white" {}
        _BaseMap ("", 2D) = "white" {}
        _Cutoff ("Cutoff", float) = 0.5
    }


CGINCLUDE
// Common code used by most of the things below
#include "UnityCG.cginc"
struct v2f {
    float4 pos : SV_POSITION;
    float2 uv : TEXCOORD0;
};
uniform float4 _MainTex_ST;
uniform float4 _MainTex_TexelSize;
uniform float4 _MainTex_MipInfo;

UNITY_DECLARE_TEX2D(_MainTex);
UNITY_DECLARE_TEX2D(_SceneViewMipcolorsTexture);

uint GetMipCount(Texture2D tex)
{
#if defined(SHADER_API_D3D11) || defined(SHADER_API_D3D12) || defined(SHADER_API_D3D11_9X) || defined(SHADER_API_XBOXONE) || defined(SHADER_API_PSSL)
    #define MIP_COUNT_SUPPORTED 1
#endif
#if (defined(SHADER_API_OPENGL) || defined(SHADER_API_VULKAN)) && !defined(SHADER_STAGE_COMPUTE)
    // OpenGL only supports textureSize for width, height, depth
    // textureQueryLevels (GL_ARB_texture_query_levels) needs OpenGL 4.3 or above and doesn't compile in compute shaders
    // tex.GetDimensions converted to textureQueryLevels
    #define MIP_COUNT_SUPPORTED 1
#endif
    // Metal doesn't support high enough OpenGL version

#if defined(MIP_COUNT_SUPPORTED)
    uint mipLevel, width, height, mipCount;
    mipLevel = width = height = mipCount = 0;
    tex.GetDimensions(mipLevel, width, height, mipCount);
    return mipCount;
#else
    return 0;
#endif
}

float4 GetStreamingMipColor(uint mipCount, float4 mipInfo)
{
    // alpha is amount to blend with source color (0.0 = use original, 1.0 = use new color)

    // mipInfo :
    // x = quality setings minStreamingMipLevel
    // y = original mip count for texture
    // z = desired on screen mip level
    // w = loaded mip level
    uint originalTextureMipCount = uint(mipInfo.y);

    // If material/shader mip info (original mip level) has not been set it’s either not a streamed texture 
    // or no renderer is updating it
    if (originalTextureMipCount == 0)
        return float4(0.0, 0.0, 1.0, 0.5);

    uint desiredMipLevel = uint(mipInfo.z);
    uint mipCountDesired = uint(originalTextureMipCount)-uint(desiredMipLevel);
    if (mipCount == 0)
    {
        // Can't calculate, use the passed value
        mipCount = originalTextureMipCount - uint(mipInfo.w);
    }

    if (mipCount < mipCountDesired)
    {
        // red tones when not at the desired mip level (reduction due to budget). Brighter is further from original, alpha 0 when at desired
        float ratioToDesired = float(mipCount) / float(mipCountDesired);
        return float4(1.0, 0.0, 0.0, 1.0 - ratioToDesired);
    }
    else if (mipCount >= originalTextureMipCount)
    {
        // original color when at (or beyond) original mip count
        return float4(1.0, 1.0, 1.0, 0.0);
    }
    else
    {
        // green tones when not at the original mip level. Brighter is closer to original, alpha 0 when at original
        float ratioToOriginal = float(mipCount) / float(originalTextureMipCount);
        return float4(0.0, 1.0, 0.0, 1.0 - ratioToOriginal);
    }
}

float3 GetDebugStreamingMipColorBlended(float3 originalColor, Texture2D tex, float4 mipInfo)
{
    uint mipCount = GetMipCount(tex);
    float4 mipColor = GetStreamingMipColor(mipCount, mipInfo);
    return lerp(originalColor, mipColor.rgb, mipColor.a);
}


v2f vert( appdata_base v ) {
    v2f o;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
    
    return o;
}

fixed4 frag(v2f i) : COLOR
{
    fixed4 col = UNITY_SAMPLE_TEX2D(_MainTex, i.uv);
    half4 res;
    res.rgb = GetDebugStreamingMipColorBlended(col.rgb, _MainTex, _MainTex_MipInfo);
    res.a = col.a;
    return res;
}

struct v2fGrass {
    float4 pos : SV_POSITION;
    fixed4 color : COLOR;
    float2 uv : TEXCOORD0;
};

fixed4 fragGrass(v2fGrass i) : COLOR
{
    fixed4 col = UNITY_SAMPLE_TEX2D(_MainTex, i.uv);
    half4 res;
    res.rgb = GetDebugStreamingMipColorBlended(col.rgb, _MainTex, _MainTex_MipInfo);
    res.a = col.a * i.color.a;
    return res;
}
ENDCG

SubShader {
    Tags { "ForceSupported" = "True" "RenderType"="Opaque" }
    Pass {
CGPROGRAM

// As both normal opaque shaders and terrain splat shaders
// have "Opaque" render type, we need to do some voodoo
// to make both work.

#pragma vertex vertWTerrain
#pragma fragment fragWTerrain
#pragma target 2.0
#pragma exclude_renderers gles

struct v2fterr {
    float4 pos : SV_POSITION;
    float2 uvnormal : TEXCOORD0;
    float4 uv[3] : TEXCOORD2;
    float nonterrain  : TEXCOORD5;
};

uniform float4 _Splat0_ST,_Splat1_ST,_Splat2_ST,_Splat3_ST,_Splat4_ST;
uniform float4 _Splat0_TexelSize,_Splat1_TexelSize,_Splat2_TexelSize,_Splat3_TexelSize,_Splat4_TexelSize;
uniform float4 _BaseMap_TexelSize;

v2fterr vertWTerrain( appdata_base v ) {
    v2fterr o;
    o.pos = UnityObjectToClipPos(v.vertex);
    // assume it's not a terrain if _Splat0_TexelSize is not set up.
    float nonterrain = _Splat0_TexelSize.z==0.0 ? 1:0;
    // collapse/don't draw terrain's add pass in this mode, since it looks really bad if first pass
    // and add pass blink depending on which gets drawn first with this replacement shader
    // TODO: make it display mips properly even for two-pass terrains. 
    o.pos *= _MainTex_TexelSize.z==0.0 && _Splat0_TexelSize.z!=0.0 ? 0 : 1;
    // normal texture UV
    o.uvnormal = TRANSFORM_TEX(v.texcoord,_MainTex);
    // terrain splat UVs
    float2 baseUV = v.texcoord.xy;
    o.uv[0].xy = baseUV;
    o.uv[0].zw = half2(0,0);
    o.uv[1].xy = TRANSFORM_TEX (baseUV, _Splat0);
    o.uv[1].zw = TRANSFORM_TEX (baseUV, _Splat1);
    o.uv[2].xy = TRANSFORM_TEX (baseUV, _Splat2);
    o.uv[2].zw = TRANSFORM_TEX (baseUV, _Splat3);
    
    o.nonterrain = nonterrain;
    return o;
}
UNITY_DECLARE_TEX2D(_Control);
UNITY_DECLARE_TEX2D(_Splat0);
UNITY_DECLARE_TEX2D(_Splat1);
UNITY_DECLARE_TEX2D(_Splat2);
UNITY_DECLARE_TEX2D(_Splat3);
UNITY_DECLARE_TEX2D(_BaseMap);
fixed4 fragWTerrain(v2fterr i) : COLOR
{
    // sample regular texture
    fixed4 colnormal = UNITY_SAMPLE_TEX2D(_MainTex, i.uvnormal);
    
    // sample splatmaps
    half4 splat_control = UNITY_SAMPLE_TEX2D(_Control, i.uv[0].xy);
    half3 splat_color = splat_control.r * UNITY_SAMPLE_TEX2D(_Splat0, i.uv[1].xy).rgb;
    splat_color += splat_control.g * UNITY_SAMPLE_TEX2D(_Splat1, i.uv[1].zw).rgb;
    splat_color += splat_control.b * UNITY_SAMPLE_TEX2D(_Splat2, i.uv[2].xy).rgb;
    splat_color += splat_control.a * UNITY_SAMPLE_TEX2D(_Splat3, i.uv[2].zw).rgb;
    
    // lerp between normal and splatmaps
    half3 col = lerp(splat_color, colnormal.rgb, (half)i.nonterrain);

    half4 res;
    // TODO: Take splat mips into account
    res.rgb = GetDebugStreamingMipColorBlended(col.rgb, _MainTex, _MainTex_MipInfo);
    res.a = colnormal.a;
    
    return res;
}
ENDCG
    }
}

SubShader {
    Tags { "ForceSupported" = "True" "RenderType"="Transparent" }
    Pass {
        Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
ENDCG
    }
}

SubShader {
    Tags { "ForceSupported" = "True" "RenderType"="TransparentCutout" }
    Pass {
        AlphaTest Greater [_Cutoff]
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
ENDCG
    }
}

SubShader {
    Tags { "ForceSupported" = "True" "RenderType"="TreeBark" }
    Pass {
CGPROGRAM
#pragma vertex vertTreeBark
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
#include "UnityCG.cginc"
#include "UnityBuiltin3xTreeLibrary.cginc"
v2f vertTreeBark (appdata_full v) {
    v2f o;
    TreeVertBark(v);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
}
ENDCG
    }
}

SubShader {
    Tags { "ForceSupported" = "True" "RenderType"="TreeLeaf" }
    Pass {
CGPROGRAM
#pragma vertex vertTreeLeaf
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
#include "UnityCG.cginc"
#include "UnityBuiltin3xTreeLibrary.cginc"
v2f vertTreeLeaf (appdata_full v) {
    v2f o;
    TreeVertLeaf (v);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
}
ENDCG
        AlphaTest GEqual [_Cutoff]
    }
}

SubShader {
    Tags { "ForceSupported" = "True" "RenderType"="TreeOpaque" }
    Pass {
CGPROGRAM
#pragma vertex vertTree
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
#include "TerrainEngine.cginc"
struct appdata {
    float4 vertex : POSITION;
    fixed4 color : COLOR;
    float2 texcoord : TEXCOORD0;
};
v2f vertTree( appdata v ) {
    v2f o;
    TerrainAnimateTree(v.vertex, v.color.w);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
}
ENDCG
    }
} 

SubShader {
    Tags { "ForceSupported" = "True" "RenderType"="TreeTransparentCutout" }
    Pass {
        Cull Off
CGPROGRAM
#pragma vertex vertTree
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
#include "TerrainEngine.cginc"
struct appdata {
    float4 vertex : POSITION;
    fixed4 color : COLOR;
    float4 texcoord : TEXCOORD0;
};
v2f vertTree( appdata v ) {
    v2f o;
    TerrainAnimateTree(v.vertex, v.color.w);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
}
ENDCG
        AlphaTest GEqual [_Cutoff]
    }
}

SubShader {
    Tags { "ForceSupported" = "True" "RenderType"="TreeBillboard" }
    Pass {
        Cull Off
        ZWrite Off
CGPROGRAM
#pragma vertex vertTree
#pragma fragment frag
#pragma target 2.0
#pragma exclude_renderers gles
#include "TerrainEngine.cginc"
v2f vertTree (appdata_tree_billboard v) {
    v2f o;
    TerrainBillboardTree(v.vertex, v.texcoord1.xy, v.texcoord.y);
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv.x = v.texcoord.x;
    o.uv.y = v.texcoord.y &gt; 0;
    return o;
}
ENDCG
        
        SetTexture [_MainTex] { combine primary, texture }
    }
}

SubShader {
    Tags { "ForceSupported" = "True" "RenderType"="GrassBillboard" }
    Pass {
        Cull Off
CGPROGRAM
#pragma vertex vertGrass
#pragma fragment fragGrass
#pragma target 2.0
#pragma exclude_renderers gles
#include "TerrainEngine.cginc"
v2fGrass vertGrass (appdata_full v) {
    v2fGrass o;
    WavingGrassBillboardVert (v);
    o.color = v.color;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
}
ENDCG
        AlphaTest Greater [_Cutoff]
    }
}

SubShader {
    Tags { "ForceSupported" = "True" "RenderType"="Grass" }
    Pass {
        Cull Off
CGPROGRAM
#pragma vertex vertGrass
#pragma fragment fragGrass
#pragma target 2.0
#pragma exclude_renderers gles
#include "TerrainEngine.cginc"
v2fGrass vertGrass (appdata_full v) {
    v2fGrass o;
    WavingGrassVert (v);
    o.color = v.color;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = v.texcoord;
    return o;
}
ENDCG
        AlphaTest Greater [_Cutoff]
    }
}

Fallback Off
}
Streaming Controller
Color space