MaxSamplers

MaxSamplers

#Overview

name: MaxSamplers

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 MaxSamplers is to define the maximum number of texture samplers that can be used in a shader for a specific platform or rendering context in Unreal Engine 5. This setting is crucial for the rendering system, particularly for shader compilation and resource management.

MaxSamplers is primarily used by the rendering subsystem, shader compiler, and material editor modules of Unreal Engine 5. It’s referenced in various parts of the engine, including the OpenGL shader compiler, D3D12 RHI, and material editing tools.

The value of this variable is typically set in the FGenericDataDrivenShaderPlatformInfo class, which manages shader platform-specific information. It’s initialized with a default value of 16 in the SetDefaultValues function and can be overridden for specific platforms through configuration files.

MaxSamplers interacts with other variables related to shader resource limits, such as MaxSRVs (Shader Resource Views) and MaxUAVs (Unordered Access Views). It’s often used in conjunction with these variables to define the overall resource constraints for shaders.

Developers must be aware that exceeding the MaxSamplers limit can lead to shader compilation failures or runtime errors. The engine performs checks to ensure that shaders don’t use more samplers than allowed, as seen in the OpenGLShaderCompiler.cpp file.

Best practices when using this variable include:

  1. Keeping track of sampler usage in materials and shaders to avoid exceeding the limit.
  2. Considering the target platforms when designing materials, as different platforms may have different MaxSamplers values.
  3. Using the material editor’s statistics to monitor sampler usage and optimize materials if necessary.
  4. Being cautious when working with cross-platform projects, as MaxSamplers may vary between platforms.
  5. Understanding that increasing MaxSamplers beyond the hardware capabilities can lead to performance issues or rendering artifacts.

#Setting Variables

#References In INI files

Location: <Workspace>/Engine/Config/Windows/DataDrivenPlatformInfo.ini:137, section: [ShaderPlatform PCD3D_SM6]

#References in C++ code

#Callsites

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

#Loc: <Workspace>/Engine/Source/Developer/ShaderFormatOpenGL/Private/OpenGLShaderCompiler.cpp:874

Scope (from outer to inner):

file
function     void BuildShaderOutputInternal

Source code excerpt:

	}

	constexpr int32 MaxSamplers = 16;

	if (Header.Bindings.NumSamplers > MaxSamplers)
	{
		ShaderOutput.bSucceeded = false;
		FShaderCompilerError& NewError = ShaderOutput.Errors.AddDefaulted_GetRef();
		NewError.StrippedErrorMessage =
			FString::Printf(TEXT("shader uses %d samplers exceeding the limit of %d"),
				Header.Bindings.NumSamplers, MaxSamplers);
	}
	else if (ShaderOutput.bSucceeded)
	{
		// Write out the header
		FMemoryWriter Ar(ShaderOutput.ShaderCode.GetWriteAccess(), true);
		Ar << Header;

#Loc: <Workspace>/Engine/Source/Editor/LevelEditor/Private/LevelEditorActions.cpp:707

Scope (from outer to inner):

file
function     bool FLevelEditorActionCallbacks::CanExecutePreviewPlatform

Source code excerpt:

		}

		// When the preview platform's DDSPI MaxSamplers is > 16 and the current RHI device has support
		// for > 16 samplers we rely on the shader compiler being able to choose an appropriate profile for the
		// preview feature level that supports > 16 samplers. On D3D12 SM5 the D3D shader compiler will use Dxc and
		// sm6.0. Vulkan SM5 also appears to handle > 16 samplers fine.
		// TODO: Look into support for Metal (MacOS).
		if ((int32)FDataDrivenShaderPlatformInfo::GetMaxSamplers(PreviewShaderPlatform) > GRHIGlobals.MaxTextureSamplers)
		{

#Loc: <Workspace>/Engine/Source/Editor/MaterialEditor/Private/MaterialEditor.cpp:2249

Scope (from outer to inner):

file
function     void FMaterialEditor::DrawMaterialInfoStrings

Source code excerpt:

		if (SamplersUsed >= 0)
		{
			int32 MaxSamplers = GetExpectedFeatureLevelMaxTextureSamplers(MaterialResource->GetFeatureLevel());

			Canvas->DrawShadowedString(
				5,
				DrawPositionY,
				*FString::Printf(TEXT("%s samplers: %u/%u"), FeatureLevel <= ERHIFeatureLevel::ES3_1 ? TEXT("Mobile texture") : TEXT("Texture"), SamplersUsed, MaxSamplers),
				FontToUse,
				SamplersUsed > MaxSamplers ? FLinearColor(1,0,0) : FLinearColor(1,1,0)
				);
			DrawPositionY += SpacingBetweenLines;
		}

		uint32 NumVSTextureSamples = 0, NumPSTextureSamples = 0;
		MaterialResource->GetEstimatedNumTextureSamples(NumVSTextureSamples, NumPSTextureSamples);

#Loc: <Workspace>/Engine/Source/Editor/MaterialEditor/Private/MaterialEditor.cpp:3088

Scope (from outer to inner):

file
function     void FMaterialEditor::UpdateMaterialinfoList_Old

Source code excerpt:

				if (SamplersUsed >= 0)
				{
					int32 MaxSamplers = GetExpectedFeatureLevelMaxTextureSamplers(MaterialResource->GetFeatureLevel());
					FString SamplersString = FString::Printf(TEXT("%s samplers: %u/%u"), FeatureLevel <= ERHIFeatureLevel::ES3_1 ? TEXT("Mobile texture") : TEXT("Texture"), SamplersUsed, MaxSamplers);
					TempMaterialInfoList.Add(MakeShareable(new FMaterialInfo(SamplersString, FLinearColor::Yellow)));
					TSharedRef<FTokenizedMessage> Line = FTokenizedMessage::Create( EMessageSeverity::Info );
					Line->AddToken( FTextToken::Create( FText::FromString( SamplersString ) ) );
					Messages.Add(Line);
				}
				

#Loc: <Workspace>/Engine/Source/Editor/MaterialEditor/Private/MaterialStatsCommon.cpp:681

Scope (from outer to inner):

file
function     void FMaterialStatsUtils::ExtractMatertialStatsInfo

Source code excerpt:

		// extract samplers info
		const int32 SamplersUsed = FMath::Max(MaterialResource->GetSamplerUsage(), 0);
		const int32 MaxSamplers = GetExpectedFeatureLevelMaxTextureSamplers(MaterialResource->GetFeatureLevel());
		OutInfo.SamplersCount.StrDescription = FString::Printf(TEXT("%u/%u"), SamplersUsed, MaxSamplers);
		OutInfo.SamplersCount.StrDescriptionLong = FString::Printf(TEXT("%s samplers: %u/%u"), TEXT("Texture"), SamplersUsed, MaxSamplers);

		// extract estimated sample info
		uint32 NumVSTextureSamples = 0, NumPSTextureSamples = 0;
		MaterialResource->GetEstimatedNumTextureSamples(NumVSTextureSamples, NumPSTextureSamples);

		OutInfo.TextureSampleCount.StrDescription = FString::Printf(TEXT("VS(%u), PS(%u)"), NumVSTextureSamples, NumPSTextureSamples);

#Loc: <Workspace>/Engine/Source/Runtime/D3D12RHI/Private/D3D12Util.cpp:1169

Scope (from outer to inner):

file
function     void FD3D12QuantizedBoundShaderState::InitShaderRegisterCounts

Source code excerpt:

{
	uint32 MaxSRVs = MAX_SRVS;
	uint32 MaxSamplers = MAX_SAMPLERS;
	uint32 MaxUAVs = MAX_UAVS;
	uint32 MaxCBs = MAX_CBS;

	// On tier 1 & 2 HW the actual descriptor table size used during the draw/dispatch must match that of the
	// root signature so we round the size up to the closest power of 2 to accomplish 2 goals: 1) keep the size of
	// the table closer to the required size to limit descriptor heap usage due to required empty descriptors,

#Loc: <Workspace>/Engine/Source/Runtime/D3D12RHI/Private/D3D12Util.cpp:1189

Scope (from outer to inner):

file
function     void FD3D12QuantizedBoundShaderState::InitShaderRegisterCounts

Source code excerpt:

	if (ResourceBindingTier <= D3D12_RESOURCE_BINDING_TIER_1)
	{
		Shader.SamplerCount = (Counts.NumSamplers > 0) ? FMath::Min(MaxSamplers, FMath::RoundUpToPowerOfTwo(Counts.NumSamplers)) : Counts.NumSamplers;
		Shader.ShaderResourceCount = (Counts.NumSRVs > 0) ? FMath::Min(MaxSRVs, FMath::RoundUpToPowerOfTwo(Counts.NumSRVs)) : Counts.NumSRVs;
	}
	else
	{
		Shader.SamplerCount = Counts.NumSamplers > 0 ? MaxSamplers : 0;
		Shader.ShaderResourceCount = Counts.NumSRVs > 0 ? MaxSRVs : 0;
	}

	if (ResourceBindingTier <= D3D12_RESOURCE_BINDING_TIER_2)
	{
		Shader.ConstantBufferCount = (Counts.NumCBs > MAX_ROOT_CBVS) ? FMath::Min(MaxCBs, FMath::RoundUpToPowerOfTwo(Counts.NumCBs)) : Counts.NumCBs;

#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/ShaderCompiler/ShaderCompiler.cpp:8374

Scope (from outer to inner):

file
function     void GlobalBeginCompileShader

Source code excerpt:


	{
		const uint32 MaxSamplers = FDataDrivenShaderPlatformInfo::GetMaxSamplers((EShaderPlatform)Target.Platform);
		SET_SHADER_DEFINE(Input.Environment, PLATFORM_MAX_SAMPLERS, MaxSamplers);
	}

	bool bForwardShading = false;
	{
		if (TargetPlatform)
		{

#Loc: <Workspace>/Engine/Source/Runtime/RHI/Private/DataDrivenShaderPlatformInfo.cpp:148

Scope (from outer to inner):

file
function     void FGenericDataDrivenShaderPlatformInfo::SetDefaultValues

Source code excerpt:

	bSupportsClipDistance = true;
	bSupportsShaderPipelines = true;
	MaxSamplers = 16;
}

void FGenericDataDrivenShaderPlatformInfo::ParseDataDrivenShaderInfo(const FConfigSection& Section, uint32 Index)
{
	FGenericDataDrivenShaderPlatformInfo& Info = Infos[Index];

#Loc: <Workspace>/Engine/Source/Runtime/RHI/Private/DataDrivenShaderPlatformInfo.cpp:293

Scope (from outer to inner):

file
function     void FGenericDataDrivenShaderPlatformInfo::ParseDataDrivenShaderInfo

Source code excerpt:

	GET_SECTION_BOOL_HELPER(bSupportsUniformBufferObjects);
	GET_SECTION_BOOL_HELPER(bRequiresBindfulUtilityShaders);
	GET_SECTION_INT_HELPER(MaxSamplers);
	GET_SECTION_BOOL_HELPER(SupportsBarycentricsIntrinsics);
	GET_SECTION_SUPPORT_HELPER(SupportsBarycentricsSemantic);
	GET_SECTION_BOOL_HELPER(bSupportsWave64);
#undef GET_SECTION_BOOL_HELPER
#undef GET_SECTION_INT_HELPER
#undef GET_SECTION_SUPPORT_HELPER

#Loc: <Workspace>/Engine/Source/Runtime/RHI/Public/DataDrivenShaderPlatformInfo.h:119

Scope (from outer to inner):

file
class        class FGenericDataDrivenShaderPlatformInfo

Source code excerpt:

	uint32 bSupportsUniformBufferObjects : 1;
	uint32 bRequiresBindfulUtilityShaders : 1;
	uint32 MaxSamplers : 8;
	uint32 SupportsBarycentricsIntrinsics : 1;
	uint32 SupportsBarycentricsSemantic : int32(ERHIFeatureSupport::NumBits);
	uint32 bSupportsWave64 : 1;

	// NOTE: When adding fields, you must also add to ParseDataDrivenShaderInfo!
	uint32 bContainsValidPlatformInfo : 1;

#Loc: <Workspace>/Engine/Source/Runtime/RHI/Public/DataDrivenShaderPlatformInfo.h:773

Scope (from outer to inner):

file
class        class FGenericDataDrivenShaderPlatformInfo
function     static const uint32 GetMaxSamplers

Source code excerpt:

	{
		check(IsValid(Platform));
		return Infos[Platform].MaxSamplers;
	}

	static FORCEINLINE_DEBUGGABLE const bool GetSupportsBarycentricsIntrinsics(const FStaticShaderPlatform Platform)
	{
		check(IsValid(Platform));
		return Infos[Platform].SupportsBarycentricsIntrinsics;