NumStreamedMips

NumStreamedMips

#Overview

name: NumStreamedMips

This variable is created as a Console Variable (cvar).

It is referenced in 13 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of NumStreamedMips is to control the number of mip levels that can be streamed for textures in Unreal Engine 5. It is primarily used for texture streaming and LOD (Level of Detail) management within the rendering system.

NumStreamedMips is primarily used by the texture streaming subsystem, which is part of the Engine module. It’s specifically utilized in the FRenderAssetStreamingManager and FStreamingRenderAsset classes, which handle the streaming of render assets, including textures.

The value of this variable is typically set in the FTextureLODGroup structure, which is part of the texture LOD settings. It can be modified through the engine’s configuration system or via console commands.

NumStreamedMips interacts with other variables related to texture streaming and LOD management, such as MinAllowedMips, MaxAllowedMips, and ResourceState.MaxNumLODs. It’s used to calculate the minimum allowed mip level for a texture.

Developers should be aware that:

  1. A value of -1 for NumStreamedMips means all mips can be streamed.
  2. The variable is used per texture group, allowing for fine-grained control over different types of textures.
  3. Changing this value can significantly impact memory usage and streaming behavior.

Best practices when using this variable include:

  1. Carefully consider the balance between memory usage and visual quality when adjusting this value.
  2. Use different settings for different texture groups based on their importance and visibility in the game.
  3. Test thoroughly after making changes, as it can affect both performance and visual quality.
  4. Consider using the console command system for dynamic adjustments during development and debugging.

#References in C++ code

#Callsites

This variable is referenced in the following C++ source code:

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Classes/Engine/TextureLODSettings.h:22

Scope (from outer to inner):

file
function     FTextureLODGroup

Source code excerpt:

		, LODBias_Smaller(-1)
		, LODBias_Smallest(-1)
		, NumStreamedMips(-1)
		, MipGenSettings(TextureMipGenSettings::TMGS_SimpleAverage)
		, MinLODSize(1)
		, MaxLODSize(4096)
		, MaxLODSize_Smaller(-1)
		, MaxLODSize_Smallest(-1)
		, MaxLODSize_VT(0)

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Classes/Engine/TextureLODSettings.h:69

Scope: file

Source code excerpt:

	/** Number of mip-levels that can be streamed. -1 means all mips can stream.	*/
	UPROPERTY()
	int32 NumStreamedMips;

	/** Defines how the the mip-map generation works, e.g. sharpening				*/
	UPROPERTY()
	TEnumAsByte<TextureMipGenSettings> MipGenSettings;

	/** Prevent LODBias from making the textures smaller than this value. Note that this does _not_ affect the smallest mip level size. */

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/StreamingManagerTexture.cpp:162

Scope (from outer to inner):

file
function     FRenderAssetStreamingManager::FRenderAssetStreamingManager

Source code excerpt:

		const TextureGroup TextureLODGroup = (TextureGroup)LODGroup;
		const FTextureLODGroup& TexGroup = UDeviceProfileManager::Get().GetActiveProfile()->GetTextureLODSettings()->GetTextureLODGroup(TextureLODGroup);
		NumStreamedMips_Texture[LODGroup] = TexGroup.NumStreamedMips;
		// For compatibility reason, Character groups are always loaded with higher priority
		Settings.HighPriorityLoad_Texture[LODGroup] = TexGroup.HighPriorityLoad || TextureLODGroup == TEXTUREGROUP_Character || TextureLODGroup == TEXTUREGROUP_CharacterSpecular || TextureLODGroup == TEXTUREGROUP_CharacterNormalMap;
	}

	// TODO: NumStreamedMips_StaticMesh, NumStreamedMips_SkeletalMesh, NumStreamedMips_LandscapeMeshMobile
	NumStreamedMips_StaticMesh.Empty(1);

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/StreamingManagerTexture.cpp:540

Scope (from outer to inner):

file
function     void FRenderAssetStreamingManager::ProcessAddedRenderAssets

Source code excerpt:

		{
			Asset->StreamingIndex = StreamingRenderAssets.Num();
			const int32* NumStreamedMips = nullptr;
			const int32 NumLODGroups = GetNumStreamedMipsArray(Asset->GetRenderAssetType(), NumStreamedMips);
			new (StreamingRenderAssets) FStreamingRenderAsset(Asset, NumStreamedMips, NumLODGroups, Settings);
		}
	}
	PendingStreamingRenderAssets.Reset();
}

void FRenderAssetStreamingManager::ConditionalUpdateStaticData()

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/StreamingManagerTexture.cpp:1242

Scope (from outer to inner):

file
function     void FRenderAssetStreamingManager::UpdateIndividualRenderAsset

Source code excerpt:

	if (!StreamingRenderAsset) return;

	const int32* NumStreamedMips;
	const int32 NumLODGroups = GetNumStreamedMipsArray(StreamingRenderAsset->RenderAssetType, NumStreamedMips);

	StreamingRenderAsset->UpdateDynamicData(NumStreamedMips, NumLODGroups, Settings, false);

	if (StreamingRenderAsset->bForceFullyLoad) // Somewhat expected at this point.
	{
		StreamingRenderAsset->WantedMips = StreamingRenderAsset->BudgetedMips = StreamingRenderAsset->MaxAllowedMips;
	}

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/StreamingManagerTexture.cpp:1360

Scope (from outer to inner):

file
function     void FRenderAssetStreamingManager::UpdateStreamingRenderAssets
lambda-function

Source code excerpt:

				}

				const int32* NumStreamedMips;
				const int32 NumLODGroups = GetNumStreamedMipsArray(StreamingRenderAsset.RenderAssetType, NumStreamedMips);

				StreamingRenderAsset.UpdateDynamicData(NumStreamedMips, NumLODGroups, Settings, bWaitForMipFading, &Packets[PacketIndex].LocalDeferredTickCBAssets); // We always use the Deferred CBs when doing the ParallelFor since those CBs are not thread safe.

				// Make a list of each texture/mesh that can potentially require additional UpdateStreamingStatus
				if (StreamingRenderAsset.RequestedMips != StreamingRenderAsset.ResidentMips)
				{
					Packets[PacketIndex].LocalInflightRenderAssets.Add(Index);
				}

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/StreamingManagerTexture.cpp:1395

Scope (from outer to inner):

file
function     void FRenderAssetStreamingManager::UpdateStreamingRenderAssets

Source code excerpt:

			STAT(int32 PreviousResidentMips = StreamingRenderAsset.ResidentMips;)

			const int32* NumStreamedMips;
			const int32 NumLODGroups = GetNumStreamedMipsArray(StreamingRenderAsset.RenderAssetType, NumStreamedMips);

			StreamingRenderAsset.UpdateDynamicData(NumStreamedMips, NumLODGroups, Settings, bWaitForMipFading, bAsync ? &DeferredTickCBAssets : nullptr);

			// Make a list of each texture/mesh that can potentially require additional UpdateStreamingStatus
			if (StreamingRenderAsset.RequestedMips != StreamingRenderAsset.ResidentMips)
			{
				InflightRenderAssets.Add(Index);
			}

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/StreamingManagerTexture.cpp:2339

Scope (from outer to inner):

file
function     bool FRenderAssetStreamingManager::HandleNumStreamedMipsCommand

Source code excerpt:

		if ( NumMips >= -1 && NumMips <= MAX_TEXTURE_MIP_COUNT )
		{
			TexGroup.NumStreamedMips = NumMips;
		}
		Ar.Logf( TEXT("%s.NumStreamedMips = %d"), UTexture::GetTextureGroupString(TextureGroup(LODGroup)), TexGroup.NumStreamedMips );
	}
	else if (LODGroupType == TEXT("StaticMesh"))
	{
		// TODO
		Ar.Logf(TEXT("NumStreamedMips command is not implemented for static mesh yet"));
	}

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/StreamingManagerTexture.cpp:2929

Scope (from outer to inner):

file
function     bool FRenderAssetStreamingManager::Exec

Source code excerpt:

		return HandleShadowmapStreamingFactorCommand( Cmd, Ar );
	}
	else if (FParse::Command(&Cmd,TEXT("NumStreamedMips")))
	{
		return HandleNumStreamedMipsCommand( Cmd, Ar );
	}
	else if (FParse::Command(&Cmd,TEXT("TrackTexture"))
		|| FParse::Command(&Cmd, TEXT("TrackRenderAsset")))
	{

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/StreamingTexture.cpp:29

Scope (from outer to inner):

file
function     FStreamingRenderAsset::FStreamingRenderAsset

Source code excerpt:

FStreamingRenderAsset::FStreamingRenderAsset(
	UStreamableRenderAsset* InRenderAsset,
	const int32* NumStreamedMips,
	int32 NumLODGroups,
	const FRenderAssetStreamingSettings& Settings)
	: RenderAsset(InRenderAsset)
	, RenderAssetType(InRenderAsset->GetRenderAssetType())
{
	UpdateStaticData(Settings);
	UpdateDynamicData(NumStreamedMips, NumLODGroups, Settings, false);

	// In the editor, some paths can recreate the FStreamingRenderAsset, which could potentiallly trigger the unkown ref heuristic.
	// To prevent this, we consider that the asset bindings where reset when creating the FStreamingRenderAsset.
	// In game, we set it to FLT_MAX so that unkown ref heurisitic can kick in immeditaly (otherwise it incurs a 5 sec penalty on async loading)
	InstanceRemovedTimestamp = GIsEditor ? FApp::GetCurrentTime() : -FLT_MAX;
	DynamicBoostFactor = 1.f;

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/StreamingTexture.cpp:167

Scope (from outer to inner):

file
function     void FStreamingRenderAsset::UpdateDynamicData

Source code excerpt:

}

void FStreamingRenderAsset::UpdateDynamicData(const int32* NumStreamedMips, int32 NumLODGroups, const FRenderAssetStreamingSettings& Settings, bool bWaitForMipFading, TArray<UStreamableRenderAsset*>* DeferredTickCBAssets)
{
	// Note that those values are read from the async task and must not be assigned temporary values!!
	if (RenderAsset)
	{
		// Get the resource state after calling UpdateStreamingStatus() since it might have updated it.
		const FStreamableRenderResourceState ResourceState = UpdateStreamingStatus(bWaitForMipFading, DeferredTickCBAssets);

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/StreamingTexture.cpp:236

Scope (from outer to inner):

file
function     void FStreamingRenderAsset::UpdateDynamicData

Source code excerpt:


		check(LODGroup < NumLODGroups);
		if (NumStreamedMips[LODGroup] > 0)
		{
			MinAllowedMips = FMath::Clamp<int32>(ResourceState.MaxNumLODs - NumStreamedMips[LODGroup], ResourceState.NumNonStreamingLODs, MaxAllowedMips);
		}
		else
		{
			MinAllowedMips = ResourceState.NumNonStreamingLODs;
		}
	}

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/StreamingTexture.h:30

Scope: file

Source code excerpt:

	FStreamingRenderAsset(
		UStreamableRenderAsset* InRenderAsset,
		const int32* NumStreamedMips,
		int32 NumLODGroups,
		const FRenderAssetStreamingSettings& Settings);

	/** Update data that should not change unless changing settings. */
	void UpdateStaticData(const FRenderAssetStreamingSettings& Settings);

	/** Update data that the engine could change through gameplay. */
	void UpdateDynamicData(const int32* NumStreamedMips, int32 NumLODGroups, const FRenderAssetStreamingSettings& Settings, bool bWaitForMipFading, TArray<UStreamableRenderAsset*>* DeferredTickCBAssets = nullptr);

	/** Lightweight version of UpdateDynamicData. */
	FStreamableRenderResourceState UpdateStreamingStatus(bool bWaitForMipFading, TArray<UStreamableRenderAsset*>* DeferredTickCBAssets = nullptr);

	/**
	 * Returns the amount of memory used by the texture/mesh given a specified number of mip-maps, in bytes.