StreamOut
StreamOut
#Overview
name: StreamOut
This variable is created as a Console Variable (cvar).
- type:
Exec
- help:
Sorry: Exec commands have no help
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:
- StreamOut is part of the asset streaming system and should be used carefully to avoid visual artifacts or performance issues.
- Improper use can lead to assets not being available when needed, causing visual glitches or increased loading times.
- It’s usually managed automatically by the engine, but can be manually controlled if necessary.
Best practices when using StreamOut include:
- Allow the engine’s streaming system to manage it automatically unless there’s a specific need for manual control.
- If manual control is needed, ensure that assets are streamed back in (using StreamIn) before they’re required for rendering.
- Be mindful of performance implications, as frequent streaming operations can impact frame rate and load times.
- Test thoroughly on target hardware to ensure streaming behavior meets performance and visual quality requirements.
- 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; }