StreamOut

StreamOut

#Overview

name: StreamOut

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

It is referenced in 21 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of StreamOut is to manage the process of unloading or reducing the quality of render assets (such as textures, skeletal meshes, and static meshes) in Unreal Engine 5. This function is part of the streaming system, which dynamically manages the memory usage and quality of assets based on various factors like distance from the camera or available system resources.

StreamOut is primarily used in the rendering and asset streaming subsystems of Unreal Engine. It’s implemented in various classes that inherit from UStreamableRenderAsset, such as UTexture2D, USkeletalMesh, UStaticMesh, UTexture2DArray, and UVolumeTexture.

The value of this variable is typically set when the engine decides to reduce the quality or unload parts of an asset. This decision is usually made by the streaming system based on factors like distance from the camera, available memory, or explicit calls from game code.

StreamOut interacts with other streaming-related variables and functions, such as StreamIn, ResidentMips, and WantedMips. These work together to manage the loading and unloading of asset data.

Developers should be aware that:

  1. StreamOut is part of the asset streaming system and should be used carefully to avoid visual artifacts or performance issues.
  2. Improper use can lead to assets not being available when needed, causing visual glitches or increased loading times.
  3. It’s usually managed automatically by the engine, but can be manually controlled if necessary.

Best practices when using StreamOut include:

  1. Allow the engine’s streaming system to manage it automatically unless there’s a specific need for manual control.
  2. If manual control is needed, ensure that assets are streamed back in (using StreamIn) before they’re required for rendering.
  3. Be mindful of performance implications, as frequent streaming operations can impact frame rate and load times.
  4. Test thoroughly on target hardware to ensure streaming behavior meets performance and visual quality requirements.
  5. Use profiling tools to monitor streaming behavior and optimize as necessary.

#References in C++ code

#Callsites

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

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Classes/Engine/SkeletalMesh.h:2520

Scope (from outer to inner):

file
class        class USkeletalMesh : public USkinnedAsset, public IInterface_CollisionDataProvider, public IInterface_AssetUserData, public INodeMappingProviderInterface

Source code excerpt:

	ENGINE_API virtual FIoFilenameHash GetMipIoFilenameHash(const int32 MipIndex) const override;
	ENGINE_API virtual bool DoesMipDataExist(const int32 MipIndex) const override;
	ENGINE_API virtual bool StreamOut(int32 NewMipCount) override;
	ENGINE_API virtual bool StreamIn(int32 NewMipCount, bool bHighPrio) override;
	ENGINE_API virtual bool HasPendingRenderResourceInitialization() const;
	virtual EStreamableRenderAssetType GetRenderAssetType() const final override { return EStreamableRenderAssetType::SkeletalMesh; }
	//~ End UStreamableRenderAsset Interface.

	/**

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Classes/Engine/StaticMesh.h:1661

Scope (from outer to inner):

file
class        class UStaticMesh : public UStreamableRenderAsset, public IInterface_CollisionDataProvider, public IInterface_AssetUserData, public IInterface_AsyncCompilation

Source code excerpt:

	ENGINE_API virtual bool DoesMipDataExist(const int32 MipIndex) const final override;
	ENGINE_API virtual bool HasPendingRenderResourceInitialization() const final override;
	ENGINE_API virtual bool StreamOut(int32 NewMipCount) final override;
	ENGINE_API virtual bool StreamIn(int32 NewMipCount, bool bHighPrio) final override;
	ENGINE_API virtual EStreamableRenderAssetType GetRenderAssetType() const final override;
	//~ End UStreamableRenderAsset Interface

	/**
	* Cancels any pending static mesh streaming actions if possible.

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Classes/Engine/StreamableRenderAsset.h:84

Scope (from outer to inner):

file
class        class UStreamableRenderAsset : public UObject
function     virtual bool StreamOut

Source code excerpt:

	* @return Whether any mips were requested to be unloaded.
	*/
	virtual bool StreamOut(int32 NewMipCount)
	{
		STREAMABLERENDERASSET_NODEFAULT(StreamOut);
		return false;
	}

	/**
	* Loads mips from disk to memory. Only usable if the asset is streamable.
	*

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Classes/Engine/Texture2D.h:100

Scope: file

Source code excerpt:

protected:

	/** Helper to manage the current pending update following a call to StreamIn() or StreamOut(). */
	friend class FTexture2DUpdate;

public:

	//~ Begin UObject Interface.
	virtual void Serialize(FArchive& Ar) override;	

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Classes/Engine/Texture2D.h:147

Scope (from outer to inner):

file
class        class UTexture2D : public UTexture

Source code excerpt:

	virtual int32 CalcCumulativeLODSize(int32 NumLODs) const final override { return CalcTextureMemorySize(NumLODs); }
	ENGINE_API int32 GetNumResidentMips() const;
	ENGINE_API virtual bool StreamOut(int32 NewMipCount) final override;
	ENGINE_API virtual bool StreamIn(int32 NewMipCount, bool bHighPrio) final override;
	//~ End UStreamableRenderAsset Interface

	/** Trivial accessors. */
	ENGINE_API int32 GetSizeX() const;
	ENGINE_API int32 GetSizeY() const;

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Classes/Engine/Texture2DArray.cpp:653

Scope (from outer to inner):

file
function     bool UTexture2DArray::StreamOut

Source code excerpt:

#endif // #if WITH_EDITOR

bool UTexture2DArray::StreamOut(int32 NewMipCount)
{
	FTexture2DArrayResource* Texture2DArrayResource = GetResource() ? GetResource()->GetTexture2DArrayResource() : nullptr;
	if (!HasPendingInitOrStreaming() && CachedSRRState.StreamOut(NewMipCount) && ensure(Texture2DArrayResource))
	{
		FTextureMipAllocator* MipAllocator = new FTexture2DArrayMipAllocator_Reallocate(this);
		PendingUpdate = new FTextureStreamOut(this, MipAllocator);
		return !PendingUpdate->IsCancelled();
	}
	return false;

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Classes/Engine/Texture2DArray.h:132

Scope (from outer to inner):

file
class        class UTexture2DArray : public UTexture

Source code excerpt:

	//~ Begin UStreamableRenderAsset Interface
	virtual int32 CalcCumulativeLODSize(int32 NumLODs) const final override { return CalcTextureMemorySize(NumLODs); }
	virtual bool StreamOut(int32 NewMipCount) final override;
	virtual bool StreamIn(int32 NewMipCount, bool bHighPrio) final override;
	//~ End UStreamableRenderAsset Interface
	int32 GetNumResidentMips() const;

};

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Classes/Engine/VolumeTexture.h:135

Scope (from outer to inner):

file
class        class UVolumeTexture : public UTexture

Source code excerpt:

	//~ Begin UStreamableRenderAsset Interface
	virtual int32 CalcCumulativeLODSize(int32 NumLODs) const final override { return CalcTextureMemorySize(NumLODs); }
	virtual bool StreamOut(int32 NewMipCount) final override;
	virtual bool StreamIn(int32 NewMipCount, bool bHighPrio) final override;
	//~ End UStreamableRenderAsset Interface

protected:

#if WITH_EDITOR

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Rendering/NaniteCoarseMeshStreamingManager.cpp:456

Scope (from outer to inner):

file
namespace    Nanite
function     void FCoarseMeshStreamingManager::UpdateResourceStreaming

Source code excerpt:

			{
				// Request stream in operation
				verify(RenderAsset.RenderAsset->StreamOut(StreamingResourceState.NumNonStreamingLODs));
				RenderAsset.RenderAsset->TickStreaming();

				RenderAsset.ResourceState = EResourceState::StreamingOut;

				break;
			}

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/SkeletalMesh.cpp:1084

Scope (from outer to inner):

file
function     bool USkeletalMesh::StreamOut

Source code excerpt:

}

bool USkeletalMesh::StreamOut(int32 NewMipCount)
{
	check(IsInGameThread());
	if (!HasPendingInitOrStreaming() && CachedSRRState.StreamOut(NewMipCount))
	{
		PendingUpdate = new FSkeletalMeshStreamOut(this);
		return !PendingUpdate->IsCancelled();
	}
	return false;
}

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/StaticMesh.cpp:7293

Scope (from outer to inner):

file
function     bool UStaticMesh::StreamOut

Source code excerpt:

}

bool UStaticMesh::StreamOut(int32 NewMipCount)
{
	check(IsInGameThread());
	if (!HasPendingInitOrStreaming() && CachedSRRState.StreamOut(NewMipCount))
	{
		// We need to keep the CPU data in non cook in order to be able for tools to work correctly.
		PendingUpdate = new FStaticMeshStreamOut(this, bAllowCPUAccess && !FPlatformProperties::HasEditorOnlyData());
		return !PendingUpdate->IsCancelled();
	}
	return false;

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

Scope (from outer to inner):

file
function     bool FRenderAssetStreamingManager::StreamOutRenderAssetData

Source code excerpt:

		const int32 CurrentSize = StreamingRenderAsset.GetSize(StreamingRenderAsset.ResidentMips);

		if (StreamingRenderAsset.RenderAsset->StreamOut(StreamingRenderAsset.MinAllowedMips))
		{
			MemoryDropped += CurrentSize - MinimalSize; 
			TempMemoryUsed += MinimalSize;

			StreamingRenderAsset.UpdateStreamingStatus(false);

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

Scope: file

Source code excerpt:

		return HandleUntrackRenderAssetCommand( Cmd, Ar );
	}
	else if (FParse::Command(&Cmd,TEXT("StreamOut")))
	{
		return HandleStreamOutCommand( Cmd, Ar );
	}
	else if (FParse::Command(&Cmd,TEXT("PauseTextureStreaming"))
		|| FParse::Command(&Cmd, TEXT("PauseRenderAssetStreaming")))
	{

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

Scope (from outer to inner):

file
function     void FStreamingRenderAsset::StreamWantedMips_Internal

Source code excerpt:

			if (LocalWantedMips < ResidentMips)
			{
				RenderAsset->StreamOut(LocalWantedMips);
			}
			else // WantedMips > ResidentMips
			{
				const bool bShouldPrioritizeAsyncIORequest = (bLocalForceFullyLoadHeuristic || bIsTerrainTexture || bLoadWithHigherPriority || IsMissingTooManyMips()) && LocalWantedMips <= LocalVisibleWantedMips;
				RenderAsset->StreamIn(LocalWantedMips, bShouldPrioritizeAsyncIORequest);
			}

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Texture2D.cpp:1947

Scope (from outer to inner):

file
function     bool UTexture2D::StreamOut

Source code excerpt:

}

bool UTexture2D::StreamOut(int32 NewMipCount)
{
	check(IsInGameThread());

	FTexture2DResource* Texture2DResource = GetResource() ? GetResource()->GetTexture2DResource() : nullptr;
	if (!HasPendingInitOrStreaming() && CachedSRRState.StreamOut(NewMipCount) && ensure(Texture2DResource))
	{
		if (Texture2DResource->bUsePartiallyResidentMips)
		{
			PendingUpdate = new FTexture2DStreamOut_Virtual(this);
		}
		// If the platform supports creating the new texture on an async thread, use that path.

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/VolumeTexture.cpp:626

Scope (from outer to inner):

file
function     bool UVolumeTexture::StreamOut

Source code excerpt:

#endif // #if WITH_EDITOR

bool UVolumeTexture::StreamOut(int32 NewMipCount)
{
	FTexture3DResource* Texture3DResource = GetResource() ? GetResource()->GetTexture3DResource() : nullptr;
	if (!HasPendingInitOrStreaming() && CachedSRRState.StreamOut(NewMipCount) && ensure(Texture3DResource))
	{
		FTextureMipAllocator* MipAllocator = nullptr;

		// FVolumeTextureMipAllocator_Virtual?
		MipAllocator = new FVolumeTextureMipAllocator_Reallocate(this);

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Public/Rendering/NaniteInterface.h:16

Scope (from outer to inner):

file
namespace    Nanite

Source code excerpt:

{
	Fallback = 0u,
	StreamOut = 1u,
};

ENGINE_API ERayTracingMode GetRayTracingMode();

ENGINE_API bool GetSupportsCustomDepthRendering();

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Public/Streaming/StreamableRenderResourceState.h:126

Scope (from outer to inner):

file
function     bool StreamOut

Source code excerpt:

	}

	FORCEINLINE bool StreamOut(int32 InLODCount)
	{
		// InLODCount can be lower than MaxNumLODs, as long as there are some LODs to yet stream in.
		if (bSupportsStreaming && InLODCount < NumResidentLODs && NumResidentLODs > NumNonStreamingLODs && NumRequestedLODs == NumResidentLODs)
		{
			NumRequestedLODs = (uint8)FMath::Max<int32>(InLODCount, NumNonStreamingLODs);
			bHasPendingLODTransitionHint = true;

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/Nanite/NaniteRayTracing.cpp:689

Scope (from outer to inner):

file
namespace    Nanite

Source code excerpt:

					if (VertexBufferOffset == 0xFFFFFFFFu || IndexBufferOffset == 0xFFFFFFFFu)
					{
						// ran out of space in StreamOut buffers
						Data.bUpdating = false;
						Data.BaseMeshDataOffset = -1;

						check(Data.StagingAuxiliaryDataOffset != INDEX_NONE);
						Data.StagingAuxiliaryDataOffset = INDEX_NONE;

#Loc: <Workspace>/Projects/Lyra/Plugins/PocketWorlds/Source/Private/PocketLevelInstance.cpp:52

Scope (from outer to inner):

file
function     void UPocketLevelInstance::StreamOut

Source code excerpt:

}

void UPocketLevelInstance::StreamOut()
{
	if (StreamingPocketLevel)
	{
		StreamingPocketLevel->SetShouldBeVisible(false);
		StreamingPocketLevel->SetShouldBeLoaded(false);
	}

#Loc: <Workspace>/Projects/Lyra/Plugins/PocketWorlds/Source/Public/PocketLevelInstance.h:32

Scope (from outer to inner):

file
class        class UPocketLevelInstance : public UObject

Source code excerpt:


	void StreamIn();
	void StreamOut();

	FDelegateHandle AddReadyCallback(FPocketLevelInstanceEvent::FDelegate Callback);
	void RemoveReadyCallback(FDelegateHandle CallbackToRemove);

	virtual class UWorld* GetWorld() const override { return World; }