r.Shadow.Virtual.ResolutionLodBiasDirectional

r.Shadow.Virtual.ResolutionLodBiasDirectional

#Overview

name: r.Shadow.Virtual.ResolutionLodBiasDirectional

The value of this variable can be defined or overridden in .ini config files. 9 .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.ResolutionLodBiasDirectional is to adjust the resolution of virtual shadow maps for directional lights in Unreal Engine 5. It is used in the rendering system, specifically for shadow mapping.

This setting variable is primarily used in the Renderer module of Unreal Engine 5, particularly in the Virtual Shadow Maps subsystem. It is referenced in the VirtualShadowMapClipmap.cpp and VirtualShadowMapCacheManager.cpp files, which are part of the virtual shadow mapping implementation.

The value of this variable is set as a console variable with a default value of -0.5f. It can be modified at runtime through the console or programmatically.

The variable interacts with another related variable, CVarVirtualShadowMapResolutionLodBiasDirectionalMoving, which is used for moving directional lights. These variables are often used together to calculate the final resolution bias for virtual shadow maps.

Developers should be aware that this variable affects the quality and performance of directional shadow maps. A lower value (e.g., -1.0) increases the resolution, potentially improving shadow quality at the cost of performance, while a higher value (e.g., 1.0) decreases the resolution, potentially improving performance at the cost of shadow quality.

Best practices when using this variable include:

  1. Adjusting it based on the specific needs of your project, balancing between shadow quality and performance.
  2. Testing different values to find the optimal balance for your target hardware.
  3. Considering the interaction with other shadow-related settings, such as r.Shadow.Virtual.MaxPhysicalPages.

The associated variable CVarVirtualShadowMapResolutionLodBiasDirectional is essentially the same as r.Shadow.Virtual.ResolutionLodBiasDirectional. It is the internal representation of the console variable in the C++ code. This variable is used directly in the rendering code to apply the resolution bias to virtual shadow map calculations for directional lights. The same considerations and best practices apply to this variable as well.

#Setting Variables

#References In INI files

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

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

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

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

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

Location: <Workspace>/Projects/Lyra/Config/DefaultScalability.ini:19, section: [ShadowQuality@0]

Location: <Workspace>/Projects/Lyra/Config/DefaultScalability.ini:22, section: [ShadowQuality@1]

Location: <Workspace>/Projects/Lyra/Config/DefaultScalability.ini:25, section: [ShadowQuality@2]

Location: <Workspace>/Projects/Lyra/Config/DefaultScalability.ini:28, section: [ShadowQuality@3]

#References in C++ code

#Callsites

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

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/VirtualShadowMaps/VirtualShadowMapClipmap.cpp:22

Scope: file

Source code excerpt:


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

static TAutoConsoleVariable<float> CVarVirtualShadowMapResolutionLodBiasDirectionalMoving(

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

Scope (from outer to inner):

file
function     FVirtualShadowMapArrayCacheManager::FVirtualShadowMapArrayCacheManager
lambda-function

Source code excerpt:

			{
				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 CVarVirtualShadowMapResolutionLodBiasDirectional. They share the same value. See the following C++ source code.

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/VirtualShadowMaps/VirtualShadowMapClipmap.cpp:21

Scope: file

Source code excerpt:

extern int32 GForceInvalidateDirectionalVSM;

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

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/VirtualShadowMaps/VirtualShadowMapClipmap.cpp:180

Scope (from outer to inner):

file
function     FVirtualShadowMapClipmap::FVirtualShadowMapClipmap

Source code excerpt:

	// just resizing the virtual shadow maps for each clipmap, but convenient for now. This means we need to additionally bias
	// which levels are present.
	ResolutionLodBias = FVirtualShadowMapArray::InterpolateResolutionBias(CVarVirtualShadowMapResolutionLodBiasDirectional.GetValueOnRenderThread(), CVarVirtualShadowMapResolutionLodBiasDirectionalMoving.GetValueOnRenderThread(), LightMobilityFactor) + FMath::Log2(LodScale);
	ResolutionLodBias += GetLightSceneInfo().Proxy->GetVSMResolutionLodBias();
	// Clamp negative absolute resolution biases as they would exceed the maximum resolution/ranges allocated
	ResolutionLodBias = FMath::Max(0.0f, ResolutionLodBias);

	WorldOrigin = CameraViewMatrices.GetViewOrigin();
	CameraToViewTarget = FVector::ZeroVector;