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:
- Keeping track of sampler usage in materials and shaders to avoid exceeding the limit.
- Considering the target platforms when designing materials, as different platforms may have different MaxSamplers values.
- Using the material editor’s statistics to monitor sampler usage and optimize materials if necessary.
- Being cautious when working with cross-platform projects, as MaxSamplers may vary between platforms.
- 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]
- INI Section:
ShaderPlatform PCD3D_SM6
- Raw value:
32
- Is Array:
False
#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;