r.Nanite.MaxPixelsPerEdge

r.Nanite.MaxPixelsPerEdge

#Overview

name: r.Nanite.MaxPixelsPerEdge

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

It is referenced in 11 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of r.Nanite.MaxPixelsPerEdge is to control the triangle edge length that the Nanite runtime targets, measured in pixels. This setting variable is primarily used in the rendering system, specifically for the Nanite geometry virtualization system in Unreal Engine 5.

The Nanite subsystem within the Renderer module relies on this setting variable. It is used in various parts of the Nanite rendering pipeline, including culling, rasterization, and virtual shadow map generation.

The value of this variable is set through a console variable (CVarNaniteMaxPixelsPerEdge) with a default value of 1.0f. It can be modified at runtime through console commands or programmatically.

This variable interacts with several other variables and systems:

  1. It’s used in conjunction with r.Nanite.DicingRate to calculate the inverse dicing rate for tessellation.
  2. It’s used in dynamic render scaling calculations for both primary and shadow rasters.
  3. It’s used in the creation of packed views for Nanite rendering.
  4. It’s used in virtual shadow map generation.

Developers should be aware that:

  1. This variable directly affects the level of detail and performance of Nanite-rendered geometry.
  2. Changing this value will impact both visual quality and rendering performance.
  3. It’s used in conjunction with other Nanite-related variables, so changing it in isolation may not have the desired effect.

Best practices when using this variable include:

  1. Use it in conjunction with profiling tools to find the right balance between visual quality and performance.
  2. Consider the target hardware when setting this value, as higher values may impact performance on less powerful systems.
  3. Test thoroughly across different types of geometry and scenes to ensure consistent results.

The associated variable CVarNaniteMaxPixelsPerEdge is the actual console variable that stores and provides access to the r.Nanite.MaxPixelsPerEdge value. It’s used throughout the code to retrieve the current value of the setting. The same considerations and best practices apply to this variable as they do to r.Nanite.MaxPixelsPerEdge.

#References in C++ code

#Callsites

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

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/Nanite/NaniteCullRaster.cpp:139

Scope: file

Source code excerpt:


TAutoConsoleVariable<float> CVarNaniteMaxPixelsPerEdge(
	TEXT("r.Nanite.MaxPixelsPerEdge"),
	1.0f,
	TEXT("The triangle edge length that the Nanite runtime targets, measured in pixels."),
	ECVF_RenderThreadSafe
	);

static TAutoConsoleVariable<int32> CVarNaniteImposterMaxPixels(

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/Nanite/NaniteCullRaster.cpp:250

Scope: file

Source code excerpt:

);

// i.e. if r.Nanite.MaxPixelsPerEdge is 1.0 and r.Nanite.PrimaryRaster.PixelsPerEdgeScaling is 20%, when heavily over budget r.Nanite.MaxPixelsPerEdge will be scaled to to 5.0
static TAutoConsoleVariable<float> CVarNanitePrimaryPixelsPerEdgeScalingPercentage(
	TEXT("r.Nanite.PrimaryRaster.PixelsPerEdgeScaling"),
	30.0f, // 100% - no scaling - set to < 100% to scale pixel error when over budget
	TEXT("Lower limit percentage to scale the Nanite primary raster MaxPixelsPerEdge value when over budget."),
	ECVF_RenderThreadSafe | ECVF_Default);

// i.e. if r.Nanite.MaxPixelsPerEdge is 1.0 and r.Nanite.ShadowRaster.PixelsPerEdgeScaling is 20%, when heavily over budget r.Nanite.MaxPixelsPerEdge will be scaled to to 5.0
static TAutoConsoleVariable<float> CVarNaniteShadowPixelsPerEdgeScalingPercentage(
	TEXT("r.Nanite.ShadowRaster.PixelsPerEdgeScaling"),
	100.0f, // 100% - no scaling - set to < 100% to scale pixel error when over budget
	TEXT("Lower limit percentage to scale the Nanite shadow raster MaxPixelsPerEdge value when over budget."),
	ECVF_RenderThreadSafe | ECVF_Default);

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/Nanite/NaniteCullRaster.cpp:288

Scope (from outer to inner):

file
function     static DynamicRenderScaling::FHeuristicSettings GetDynamicNaniteScalingPrimarySettings

Source code excerpt:

	DynamicRenderScaling::FHeuristicSettings BucketSetting;
	BucketSetting.Model = DynamicRenderScaling::EHeuristicModel::Linear;
	BucketSetting.bModelScalesWithPrimaryScreenPercentage = false; // r.Nanite.MaxPixelsPerEdge is not scaled by dynamic resolution of the primary view
	BucketSetting.MinResolutionFraction = DynamicRenderScaling::PercentageToFraction(PixelsPerEdgeScalingPercentage);
	BucketSetting.MaxResolutionFraction = DynamicRenderScaling::PercentageToFraction(100.0f);
	BucketSetting.BudgetMs = CVarNanitePrimaryTimeBudgetMs.GetValueOnAnyThread();
	BucketSetting.ChangeThreshold = DynamicRenderScaling::PercentageToFraction(1.0f);
	BucketSetting.TargetedHeadRoom = DynamicRenderScaling::PercentageToFraction(5.0f); // 5% headroom
	BucketSetting.UpperBoundQuantization = DynamicRenderScaling::FHeuristicSettings::kDefaultUpperBoundQuantization;

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/Nanite/NaniteCullRaster.cpp:304

Scope (from outer to inner):

file
function     static DynamicRenderScaling::FHeuristicSettings GetDynamicNaniteScalingShadowSettings

Source code excerpt:

	DynamicRenderScaling::FHeuristicSettings BucketSetting;
	BucketSetting.Model = DynamicRenderScaling::EHeuristicModel::Linear;
	BucketSetting.bModelScalesWithPrimaryScreenPercentage = false; // r.Nanite.MaxPixelsPerEdge is not scaled by dynamic resolution of the primary view
	BucketSetting.MinResolutionFraction = DynamicRenderScaling::PercentageToFraction(PixelsPerEdgeScalingPercentage);
	BucketSetting.MaxResolutionFraction = DynamicRenderScaling::PercentageToFraction(100.0f);
	BucketSetting.BudgetMs = CVarNaniteShadowTimeBudgetMs.GetValueOnAnyThread();
	BucketSetting.ChangeThreshold = DynamicRenderScaling::PercentageToFraction(1.0f);
	BucketSetting.TargetedHeadRoom = DynamicRenderScaling::PercentageToFraction(5.0f); // 5% headroom
	BucketSetting.UpperBoundQuantization = DynamicRenderScaling::FHeuristicSettings::kDefaultUpperBoundQuantization;

#Associated Variable and Callsites

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

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/Nanite/NaniteCullRaster.cpp:138

Scope: file

Source code excerpt:

);

TAutoConsoleVariable<float> CVarNaniteMaxPixelsPerEdge(
	TEXT("r.Nanite.MaxPixelsPerEdge"),
	1.0f,
	TEXT("The triangle edge length that the Nanite runtime targets, measured in pixels."),
	ECVF_RenderThreadSafe
	);

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/Nanite/NaniteCullRaster.cpp:4382

Scope (from outer to inner):

file
namespace    Nanite
function     FBinningData FRenderer::AddPass_Rasterize
lambda-function

Source code excerpt:

		RasterPassParameters->TessellationTable_Offsets	= GTessellationTable.Offsets.SRV;
		RasterPassParameters->TessellationTable_VertsAndIndexes	= GTessellationTable.VertsAndIndexes.SRV;
		RasterPassParameters->InvDiceRate				= CVarNaniteMaxPixelsPerEdge.GetValueOnRenderThread() / CVarNaniteDicingRate.GetValueOnRenderThread();
		RasterPassParameters->MaxPatchesPerGroup		= GetMaxPatchesPerGroup();
		RasterPassParameters->MeshPass					= Configuration.bIsLumenCapture ? ENaniteMeshPass::LumenCardCapture : ENaniteMeshPass::BasePass;
		RasterPassParameters->VirtualShadowMap			= VirtualTargetParameters;

		RasterPassParameters->OutStatsBuffer			= GraphBuilder.CreateUAV(StatsBuffer, ERDGUnorderedAccessViewFlags::SkipBarrier);

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/Nanite/NaniteCullRaster.cpp:4616

Scope (from outer to inner):

file
namespace    Nanite

Source code excerpt:

		PassParameters->TessellationTable_Offsets			= GTessellationTable.Offsets.SRV;
		PassParameters->TessellationTable_VertsAndIndexes	= GTessellationTable.VertsAndIndexes.SRV;
		PassParameters->InvDiceRate					= CVarNaniteMaxPixelsPerEdge.GetValueOnRenderThread() / CVarNaniteDicingRate.GetValueOnRenderThread();

		PassParameters->RWVisiblePatches			= GraphBuilder.CreateUAV( VisiblePatches );
		PassParameters->RWVisiblePatchesArgs		= GraphBuilder.CreateUAV( VisiblePatchesArgs );
		PassParameters->VisiblePatchesSize			= VisiblePatches->GetSize() / 16;

		PassParameters->OutStatsBuffer				= GNaniteShowStats != 0u ? GraphBuilder.CreateUAV(StatsBuffer) : nullptr;

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/Nanite/NaniteShared.cpp:17

Scope: file

Source code excerpt:

IMPLEMENT_STATIC_AND_SHADER_UNIFORM_BUFFER_STRUCT(FNaniteRayTracingUniformParameters, "NaniteRayTracing", NaniteRayTracing);

extern TAutoConsoleVariable<float> CVarNaniteMaxPixelsPerEdge;
extern TAutoConsoleVariable<float> CVarNaniteMinPixelsPerEdgeHW;

// Optimized compute dual depth export pass on supported platforms.
int32 GNaniteExportDepth = 1;
static FAutoConsoleVariableRef CVarNaniteExportDepth(
	TEXT("r.Nanite.ExportDepth"),

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/Nanite/NaniteShared.cpp:117

Scope (from outer to inner):

file
namespace    Nanite
function     FPackedView CreatePackedView

Source code excerpt:

	const FVector4f ViewSizeAndInvSize(ViewRect.Width(), ViewRect.Height(), 1.0f / float(ViewRect.Width()), 1.0f / float(ViewRect.Height()));

	const float NaniteMaxPixelsPerEdge = CVarNaniteMaxPixelsPerEdge.GetValueOnRenderThread() * Params.MaxPixelsPerEdgeMultipler;
	const float NaniteMinPixelsPerEdgeHW = CVarNaniteMinPixelsPerEdgeHW.GetValueOnRenderThread();
	
	const FVector CullingViewOrigin = Params.bUseCullingViewOverrides ? Params.CullingViewOrigin : Params.ViewMatrices.GetViewOrigin();
	// We bake the view lod scales into ScreenMultiple since the two things are always used together.
	const float ViewDistanceLODScale = GetCachedScalabilityCVars().StaticMeshLODDistanceScale * Params.ViewLODDistanceFactor;
	const float ScreenMultiple = FMath::Max(Params.ViewMatrices.GetProjectionMatrix().M[0][0], Params.ViewMatrices.GetProjectionMatrix().M[1][1]) / ViewDistanceLODScale;

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/VirtualShadowMaps/VirtualShadowMapArray.cpp:38

Scope: file

Source code excerpt:

extern int32 GForceInvalidateDirectionalVSM;
extern int32 GVSMMaxPageAgeSinceLastRequest;
extern TAutoConsoleVariable<float> CVarNaniteMaxPixelsPerEdge;
extern TAutoConsoleVariable<float> CVarNaniteMinPixelsPerEdgeHW;

int32 GVSMShowLightDrawEvents = 0;
FAutoConsoleVariableRef CVarVSMShowLightDrawEvents(
	TEXT("r.Shadow.Virtual.ShowLightDrawEvents"),
	GVSMShowLightDrawEvents,

#Loc: <Workspace>/Engine/Source/Runtime/Renderer/Private/VirtualShadowMaps/VirtualShadowMapArray.cpp:1985

Scope (from outer to inner):

file
function     void FVirtualShadowMapArray::CreateMipViews

Source code excerpt:

	const int32 NumPrimaryViews = Views.Num();

	const float NaniteMaxPixelsPerEdge   = CVarNaniteMaxPixelsPerEdge.GetValueOnRenderThread();
	const float NaniteMinPixelsPerEdgeHW = CVarNaniteMinPixelsPerEdgeHW.GetValueOnRenderThread();

	// 1. create derivative views for each of the Mip levels, 
	Views.AddDefaulted( NumPrimaryViews * ( FVirtualShadowMap::MaxMipLevels - 1) );

	int32 MaxMips = 0;