MemoryMargin
MemoryMargin
#Overview
name: MemoryMargin
The value of this variable can be defined or overridden in .ini config files. 1
.ini config file referencing this setting variable.
It is referenced in 12
C++ source files.
#Summary
#Usage in the C++ source code
The purpose of MemoryMargin is to define a safety buffer in the render asset streaming system, specifically for texture and mesh streaming in Unreal Engine 5. It represents the amount of memory to leave free in the render asset pool to account for temporary allocations and system overhead.
This setting variable is primarily used in the Engine’s rendering and streaming subsystems, particularly in the AsyncTextureStreaming and StreamingManagerTexture modules.
The value of MemoryMargin is set during the initialization of the FRenderAssetStreamingManager. It’s read from the engine configuration file (GEngineIni) under the “TextureStreaming” section with the key “MemoryMargin”. The value is then converted from megabytes to bytes.
MemoryMargin interacts with several other variables in the streaming system, such as:
- PoolSize: The total size of the render asset pool
- TempMemoryBudget: Allowed temporary memory for changing mip counts
- MemoryBudget: Available memory for textures and meshes
Developers should be aware that:
- MemoryMargin affects the actual available memory for streaming assets.
- It’s used in calculations to determine when to reset mip bias and adjust the memory budget.
- It’s part of the safety pool in streaming statistics.
Best practices when using this variable include:
- Carefully consider the value set in the configuration file, as it directly impacts streaming performance.
- Monitor streaming statistics to ensure the margin is appropriate for your game’s needs.
- Adjust the value if you notice frequent memory allocation issues or underutilization of available memory.
- Consider platform-specific requirements when setting this value, as memory constraints may vary.
#Setting Variables
#References In INI files
Location: <Workspace>/Engine/Config/BaseEngine.ini:1812, section: [TextureStreaming]
- INI Section:
TextureStreaming
- Raw value:
5
- Is Array:
False
#References in C++ code
#Callsites
This variable is referenced in the following C++ source code:
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/AsyncTextureStreaming.cpp:558
Scope (from outer to inner):
file
function void FRenderAssetStreamingMipCalcTask::UpdateBudgetedMips_Async
Source code excerpt:
bool bResetMipBias = false;
if (PerfectWantedMipsBudgetResetThresold - MemoryBudgeted - MeshMemoryBudgeted > TempMemoryBudget + MemoryMargin)
{
// Reset the budget tradeoffs if the required pool size shrinked significantly.
PerfectWantedMipsBudgetResetThresold = MemoryBudgeted;
bResetMipBias = true;
}
else if (MemoryBudgeted + MeshMemoryBudgeted > PerfectWantedMipsBudgetResetThresold)
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/AsyncTextureStreaming.cpp:572
Scope (from outer to inner):
file
function void FRenderAssetStreamingMipCalcTask::UpdateBudgetedMips_Async
Source code excerpt:
const int64 NonStreamingRenderAssetMemory = AllocatedMemory - MemoryUsed + MemoryUsedByNonTextures;
int64 AvailableMemoryForStreaming = PoolSize - NonStreamingRenderAssetMemory - MemoryMargin;
// If the platform defines a max VRAM usage, check if the pool size must be reduced,
// but also check if it would be safe to some of the NonStreamingRenderAssetMemory from the pool size computation.
// The later helps significantly in low budget settings, where NonStreamingRenderAssetMemory would take too much of the pool.
if (GPoolSizeVRAMPercentage > 0 && TotalGraphicsMemory > 0)
{
const int64 UsableVRAM = FMath::Max<int64>(TotalGraphicsMemory * GPoolSizeVRAMPercentage / 100, TotalGraphicsMemory - Settings.VRAMPercentageClamp * 1024ll * 1024ll);
const int64 UsedVRAM = (int64)GRHIGlobals.NonStreamingTextureMemorySizeInKB * 1024ll + NonStreamingRenderAssetMemory; // Add any other...
const int64 AvailableVRAMForStreaming = FMath::Min<int64>(UsableVRAM - UsedVRAM - MemoryMargin, PoolSize);
if (Settings.bLimitPoolSizeToVRAM || AvailableVRAMForStreaming > AvailableMemoryForStreaming)
{
AvailableMemoryForStreaming = AvailableVRAMForStreaming;
}
}
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/AsyncTextureStreaming.cpp:596
Scope (from outer to inner):
file
function void FRenderAssetStreamingMipCalcTask::UpdateBudgetedMips_Async
Source code excerpt:
MemoryBudget = FMath::Max<int64>(AvailableMemoryForStreaming, 0);
}
else if (AvailableMemoryForStreaming - MemoryBudget > TempMemoryBudget + MemoryMargin)
{
// Increase size considering that the variation does not come from temp memory or allocator overhead (or other recurring cause).
// It's unclear how much temp memory is actually in there, but the value will decrease if temp memory increases.
MemoryBudget = AvailableMemoryForStreaming;
bResetMipBias = true;
}
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/AsyncTextureStreaming.cpp:947
Scope (from outer to inner):
file
function void FRenderAssetStreamingMipCalcTask::UpdateStats_Async
Source code excerpt:
Stats.UsedStreamingPool = 0;
Stats.SafetyPool = MemoryMargin;
Stats.TemporaryPool = TempMemoryBudget;
Stats.StreamingPool = MemoryBudget;
Stats.NonStreamingMips = AllocatedMemory;
Stats.RequiredPool = 0;
Stats.VisibleMips = 0;
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/AsyncTextureStreaming.cpp:1083
Scope (from outer to inner):
file
function void FRenderAssetStreamingMipCalcTask::UpdateCSVOnlyStats_Async
Source code excerpt:
Stats.RenderAssetPool = PoolSize;
Stats.SafetyPool = MemoryMargin;
Stats.TemporaryPool = TempMemoryBudget;
Stats.StreamingPool = MemoryBudget;
Stats.NonStreamingMips = AllocatedMemory;
Stats.RequiredPool = 0;
Stats.CachedMips = 0;
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/AsyncTextureStreaming.h:148
Scope (from outer to inner):
file
class class FRenderAssetStreamingMipCalcTask : public FNonAbandonableTask
function void Reset
Source code excerpt:
PoolSize = InPoolSize;
TempMemoryBudget = InTempMemoryBudget;
MemoryMargin = InMemoryMargin;
bAbort = false;
}
/** Notifies the async work that it should abort the thread ASAP. */
void Abort() { bAbort = true; }
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/AsyncTextureStreaming.h:234
Scope (from outer to inner):
file
class class FRenderAssetStreamingMipCalcTask : public FNonAbandonableTask
Source code excerpt:
/** How much temp memory is allowed (temp memory is taken when changing mip count). */
int64 MemoryMargin;
/** How much memory is available for textures/meshes. */
int64 MemoryBudget;
/** How much memory is available for meshes if a separate pool is used. */
int64 MeshMemoryBudget;
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/StreamingManagerTexture.cpp:118
Scope (from outer to inner):
file
function FRenderAssetStreamingManager::FRenderAssetStreamingManager
Source code excerpt:
, bUseDynamicStreaming( false )
, BoostPlayerTextures( 3.0f )
, MemoryMargin(0)
, EffectiveStreamingPoolSize(0)
, MemoryOverBudget(0)
, MaxEverRequired(0)
, bPauseRenderAssetStreaming(false)
, LastWorldUpdateTime(GIsEditor ? -FLT_MAX : 0) // In editor, visibility is not taken into consideration.
, LastWorldUpdateTime_MipCalcTask(LastWorldUpdateTime)
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/StreamingManagerTexture.cpp:128
Scope (from outer to inner):
file
function FRenderAssetStreamingManager::FRenderAssetStreamingManager
Source code excerpt:
// Read settings from ini file.
int32 TempInt;
verify( GConfig->GetInt( TEXT("TextureStreaming"), TEXT("MemoryMargin"), TempInt, GEngineIni ) );
MemoryMargin = TempInt;
verify( GConfig->GetFloat( TEXT("TextureStreaming"), TEXT("LightmapStreamingFactor"), GLightmapStreamingFactor, GEngineIni ) );
verify( GConfig->GetFloat( TEXT("TextureStreaming"), TEXT("ShadowmapStreamingFactor"), GShadowmapStreamingFactor, GEngineIni ) );
int32 PoolSizeIniSetting = 0;
GConfig->GetInt(TEXT("TextureStreaming"), TEXT("PoolSize"), PoolSizeIniSetting, GEngineIni);
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/StreamingManagerTexture.cpp:156
Scope (from outer to inner):
file
function FRenderAssetStreamingManager::FRenderAssetStreamingManager
Source code excerpt:
// Convert from MByte to byte.
MemoryMargin *= 1024 * 1024;
for ( int32 LODGroup=0; LODGroup < TEXTUREGROUP_MAX; ++LODGroup )
{
const TextureGroup TextureLODGroup = (TextureGroup)LODGroup;
const FTextureLODGroup& TexGroup = UDeviceProfileManager::Get().GetActiveProfile()->GetTextureLODSettings()->GetTextureLODGroup(TextureLODGroup);
NumStreamedMips_Texture[LODGroup] = TexGroup.NumStreamedMips;
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/StreamingManagerTexture.cpp:713
Scope (from outer to inner):
file
function void FRenderAssetStreamingManager::PrepareAsyncTask
Source code excerpt:
{
const int64 TempMemoryBudget = static_cast<int64>(Settings.MaxTempMemoryAllowed) * 1024 * 1024;
AsyncTask.Reset(Stats.TotalGraphicsMemory, Stats.StreamingMemorySize, Stats.TexturePoolSize, TempMemoryBudget, MemoryMargin);
}
else
{
// Temp must be smaller since membudget only updates if it has a least temp memory available.
AsyncTask.Reset(0, Stats.StreamingMemorySize, MAX_int64, MAX_int64 / 2, 0);
}
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/Streaming/StreamingManagerTexture.h:395
Scope: file
Source code excerpt:
/** Amount of memory to leave free in the render asset pool. */
int64 MemoryMargin;
/** The actual memory pool size available to stream textures/meshes, excludes non-streaming texture/mesh, temp memory (for streaming mips), memory margin (allocator overhead). */
int64 EffectiveStreamingPoolSize;
// Stats we need to keep across frames as we only iterate over a subset of textures.