r.Shadow.Virtual.ResolutionLodBiasLocal

r.Shadow.Virtual.ResolutionLodBiasLocal

#Overview

name: r.Shadow.Virtual.ResolutionLodBiasLocal

The value of this variable can be defined or overridden in .ini config files. 5 .ini config files referencing this setting variable.

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

It is referenced in 4 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of r.Shadow.Virtual.ResolutionLodBiasLocal is to adjust the resolution of virtual shadow maps for local lights in Unreal Engine 5’s rendering system. It applies a bias to the LOD (Level of Detail) calculations for local lights, allowing developers to fine-tune the shadow quality and performance trade-off.

This setting variable is primarily used by the Unreal Engine 5 rendering system, specifically in the virtual shadow mapping subsystem. It’s referenced in the Renderer module, particularly in the shadow scene rendering and virtual shadow map cache management components.

The value of this variable is set through the console variable system, initialized with a default value of 0.0f. It can be modified at runtime using console commands or through engine configuration files.

The associated variable CVarResolutionLodBiasLocal interacts directly with r.Shadow.Virtual.ResolutionLodBiasLocal, as they share the same value. This variable is used in the GetResolutionLODBiasLocal function to calculate the final resolution LOD bias for local lights.

Developers should be aware that this variable affects the visual quality and performance of shadow rendering for local lights. A negative value increases shadow resolution (e.g., -1.0 doubles it), while a positive value decreases it (e.g., 1.0 halves it).

Best practices when using this variable include:

  1. Adjusting it carefully to balance visual quality and performance.
  2. Testing different values in various lighting scenarios to find the optimal setting for your project.
  3. Considering the interaction with other shadow-related settings, such as r.Shadow.Virtual.ResolutionLodBiasLocalMoving for moving lights.
  4. Monitoring performance and visual artifacts, especially when pushing the limits of shadow resolution.

Regarding the associated variable CVarResolutionLodBiasLocal:

#Setting Variables

#References In INI files

Location: <Workspace>/Engine/Config/BaseScalability.ini:144, section: [ShadowQuality@0]

Location: <Workspace>/Engine/Config/BaseScalability.ini:168, section: [ShadowQuality@1]

Location: <Workspace>/Engine/Config/BaseScalability.ini:195, section: [ShadowQuality@2]

Location: <Workspace>/Engine/Config/BaseScalability.ini:222, section: [ShadowQuality@3]

Location: <Workspace>/Engine/Config/BaseScalability.ini:249, section: [ShadowQuality@Cine]

#References in C++ code

#Callsites

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

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/Shadows/ShadowSceneRenderer.cpp:63

Scope: file

Source code excerpt:


static TAutoConsoleVariable<float> CVarResolutionLodBiasLocal(
	TEXT("r.Shadow.Virtual.ResolutionLodBiasLocal"),
	0.0f,
	TEXT("Bias applied to LOD calculations for local lights. -1.0 doubles resolution, 1.0 halves it and so on."),
	ECVF_Scalability | ECVF_RenderThreadSafe
);

static TAutoConsoleVariable<float> CVarResolutionLodBiasLocalMoving(

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/VirtualShadowMaps/VirtualShadowMapCacheManager.cpp:551

Scope (from outer to inner):

file
function     FVirtualShadowMapArrayCacheManager::FVirtualShadowMapArrayCacheManager
lambda-function

Source code excerpt:

			if (!bLoggedPageOverflow)
			{
				static const auto* CVarResolutionLodBiasLocalPtr = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.Shadow.Virtual.ResolutionLodBiasLocal"));
				static const auto* CVarResolutionLodBiasDirectionalPtr = IConsoleManager::Get().FindTConsoleVariableDataFloat(TEXT("r.Shadow.Virtual.ResolutionLodBiasDirectional"));

				UE_LOG(LogRenderer, Warning, TEXT("Virtual Shadow Map Page Pool overflow (%d page allocations were not served), this will produce visual artifacts (missing shadow), increase the page pool limit or reduce resolution bias to avoid.\n")
					TEXT(" See r.Shadow.Virtual.MaxPhysicalPages (%d), r.Shadow.Virtual.ResolutionLodBiasLocal (%.2f), r.Shadow.Virtual.ResolutionLodBiasDirectional (%.2f), Global Resolution Lod Bias (%.2f)"),
					-LastFreePhysicalPages,
					MaxPhysicalPages,
					CVarResolutionLodBiasLocalPtr->GetValueOnRenderThread(),
					CVarResolutionLodBiasDirectionalPtr->GetValueOnRenderThread(),
					GlobalResolutionLodBias);

				bLoggedPageOverflow = true;
			}
			LastOverflowTime = float(FGameTime::GetTimeSinceAppStart().GetRealTimeSeconds());
#endif
		}
#if !UE_BUILD_SHIPPING
		else
		{
			bLoggedPageOverflow = false;
		}
#endif
	});

#if !UE_BUILD_SHIPPING
	// Handle message with stats sent back from GPU whenever stats are enabled
	StatsFeedbackSocket = GPUMessage::RegisterHandler(TEXT("Shadow.Virtual.StatsFeedback"), [this](GPUMessage::FReader Message)
	{
		// Culling stats
		int32 NaniteNumTris = Message.Read<int32>(0);
		int32 NanitePostCullNodeCount = Message.Read<int32>(0);
		int32 NonNanitePostCullInstanceCount = Message.Read<int32>(0);

		CSV_CUSTOM_STAT(VSM, NaniteNumTris, NaniteNumTris, ECsvCustomStatOp::Set);
		CSV_CUSTOM_STAT(VSM, NanitePostCullNodeCount, NanitePostCullNodeCount, ECsvCustomStatOp::Set);
		CSV_CUSTOM_STAT(VSM, NonNanitePostCullInstanceCount, NonNanitePostCullInstanceCount, ECsvCustomStatOp::Set);

		// Large page area items
		LastLoggedPageOverlapAppTime.SetNumZeroed(Scene->GetMaxPersistentPrimitiveIndex());
		float RealTimeSeconds = float(FGameTime::GetTimeSinceAppStart().GetRealTimeSeconds());

		TConstArrayView<uint32> PageAreaDiags = Message.ReadCount(FVirtualShadowMapArray::MaxPageAreaDiagnosticSlots * 2);
		for (int32 Index = 0; Index < PageAreaDiags.Num(); Index += 2)
		{
			uint32 Overlap = PageAreaDiags[Index];
			uint32 PersistentPrimitiveId = PageAreaDiags[Index + 1];
			int32 PrimtiveIndex = Scene->GetPrimitiveIndex(FPersistentPrimitiveIndex{ int32(PersistentPrimitiveId) });
			if (Overlap > 0 && PrimtiveIndex != INDEX_NONE)
			{
				if (RealTimeSeconds - LastLoggedPageOverlapAppTime[PersistentPrimitiveId] > 5.0f)
				{
					LastLoggedPageOverlapAppTime[PersistentPrimitiveId] = RealTimeSeconds;
					UE_LOG(LogRenderer, Warning, TEXT("Non-Nanite VSM page overlap performance Warning, %d, %s, %s"), Overlap, *Scene->Primitives[PrimtiveIndex]->GetOwnerActorNameOrLabelForDebuggingOnly(), *Scene->Primitives[PrimtiveIndex]->GetFullnameForDebuggingOnly());
				}
				LargePageAreaItems.Add(PersistentPrimitiveId, FLargePageAreaItem{ Overlap, RealTimeSeconds });
			}
		}
	});
#endif

#if !UE_BUILD_SHIPPING
	ScreenMessageDelegate = FRendererOnScreenNotification::Get().AddLambda([this](TMultiMap<FCoreDelegates::EOnScreenMessageSeverity, FText >& OutMessages)
	{
		float RealTimeSeconds = float(FGameTime::GetTimeSinceAppStart().GetRealTimeSeconds());

		// Show for ~5s after last overflow
		if (LastOverflowTime >= 0.0f && RealTimeSeconds - LastOverflowTime < 5.0f)
		{
			OutMessages.Add(FCoreDelegates::EOnScreenMessageSeverity::Warning, FText::FromString(FString::Printf(TEXT("Virtual Shadow Map Page Pool overflow detected (%0.0f seconds ago)"), RealTimeSeconds - LastOverflowTime)));
		}

		for (const auto& Item : LargePageAreaItems)
		{
			int32 PrimtiveIndex = Scene->GetPrimitiveIndex(FPersistentPrimitiveIndex{ int32(Item.Key) });
			uint32 Overlap = Item.Value.PageArea;
			if (PrimtiveIndex != INDEX_NONE && RealTimeSeconds - Item.Value.LastTimeSeen < 2.5f)
			{
				OutMessages.Add(FCoreDelegates::EOnScreenMessageSeverity::Warning, FText::FromString(FString::Printf(TEXT("Non-Nanite VSM page overlap performance Warning: Primitive '%s' overlapped %d Pages"), *Scene->Primitives[PrimtiveIndex]->GetOwnerActorNameOrLabelForDebuggingOnly(), Overlap)));
			}

#Associated Variable and Callsites

This variable is associated with another variable named CVarResolutionLodBiasLocal. They share the same value. See the following C++ source code.

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/Shadows/ShadowSceneRenderer.cpp:62

Scope: file

Source code excerpt:

);

static TAutoConsoleVariable<float> CVarResolutionLodBiasLocal(
	TEXT("r.Shadow.Virtual.ResolutionLodBiasLocal"),
	0.0f,
	TEXT("Bias applied to LOD calculations for local lights. -1.0 doubles resolution, 1.0 halves it and so on."),
	ECVF_Scalability | ECVF_RenderThreadSafe
);

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/Shadows/ShadowSceneRenderer.cpp:185

Scope (from outer to inner):

file
function     static float GetResolutionLODBiasLocal

Source code excerpt:

{
	return FVirtualShadowMapArray::InterpolateResolutionBias(
		CVarResolutionLodBiasLocal.GetValueOnRenderThread(),
		CVarResolutionLodBiasLocalMoving.GetValueOnRenderThread(),
		LightMobilityFactor) + LightLODBias;
}

FVirtualShadowMapProjectionShaderData FShadowSceneRenderer::GetLocalLightProjectionShaderData(
	float ResolutionLODBiasLocal,