Files
UnrealEngine/Engine/Source/Editor/MaterialEditor/Private/MaterialStatsCommon.cpp
2025-05-18 13:04:45 +08:00

677 lines
27 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
//
#include "MaterialStatsCommon.h"
#include "EngineGlobals.h"
#include "MaterialStats.h"
#include "LocalVertexFactory.h"
#include "GPUSkinVertexFactory.h"
#include "RenderUtils.h"
#include "MaterialEditorSettings.h"
#include "RHIShaderFormatDefinitions.inl"
#include "DataDrivenShaderPlatformInfo.h"
#include "ShaderCompilerCore.h"
#include "Styling/StyleColors.h"
/***********************************************************************************************************************/
/*begin FMaterialResourceStats functions*/
void FMaterialResourceStats::SetupExtraCompilationSettings(const EShaderPlatform Platform, FExtraShaderCompilerSettings& Settings) const
{
Settings.bExtractShaderSource = true;
FMaterialStatsUtils::GetPlatformOfflineCompilerSettings(Platform, Settings);
}
/*end FMaterialResourceStats functions*/
/***********************************************************************************************************************/
/***********************************************************************************************************************/
/*begin FMaterialStatsUtils */
TSharedPtr<FMaterialStats> FMaterialStatsUtils::CreateMaterialStats(class IMaterialEditor* MaterialEditor, const bool bShowMaterialInstancesMenu, const bool bAllowIgnoringCompilationErrors)
{
TSharedPtr<FMaterialStats> MaterialStats = MakeShareable(new FMaterialStats());
MaterialStats->Initialize(MaterialEditor, bShowMaterialInstancesMenu, bAllowIgnoringCompilationErrors);
return MaterialStats;
}
FString FMaterialStatsUtils::MaterialQualityToString(const EMaterialQualityLevel::Type Quality)
{
FString StrQuality;
switch (Quality)
{
case EMaterialQualityLevel::High:
StrQuality = TEXT("High Quality");
break;
case EMaterialQualityLevel::Medium:
StrQuality = TEXT("Medium Quality");
break;
case EMaterialQualityLevel::Low:
StrQuality = TEXT("Low Quality");
break;
case EMaterialQualityLevel::Epic:
StrQuality = TEXT("Epic Quality");
break;
}
return StrQuality;
}
FString FMaterialStatsUtils::MaterialQualityToShortString(const EMaterialQualityLevel::Type Quality)
{
FString StrQuality;
switch (Quality)
{
case EMaterialQualityLevel::High:
StrQuality = TEXT("High");
break;
case EMaterialQualityLevel::Medium:
StrQuality = TEXT("Medium");
break;
case EMaterialQualityLevel::Low:
StrQuality = TEXT("Low");
break;
case EMaterialQualityLevel::Epic:
StrQuality = TEXT("Epic");
break;
}
return StrQuality;
}
EMaterialQualityLevel::Type FMaterialStatsUtils::StringToMaterialQuality(const FString& StrQuality)
{
if (StrQuality.Equals(TEXT("High Quality")))
{
return EMaterialQualityLevel::High;
}
else if (StrQuality.Equals(TEXT("Medium Quality")))
{
return EMaterialQualityLevel::Medium;
}
else if (StrQuality.Equals(TEXT("Low Quality")))
{
return EMaterialQualityLevel::Low;
}
else if (StrQuality.Equals(TEXT("Epic Quality")))
{
return EMaterialQualityLevel::Epic;
}
return EMaterialQualityLevel::Num;
}
FString FMaterialStatsUtils::GetPlatformTypeName(const EPlatformCategoryType InEnumValue)
{
FString PlatformName;
switch (InEnumValue)
{
case EPlatformCategoryType::Desktop:
PlatformName = FString("Desktop");
break;
case EPlatformCategoryType::Android:
PlatformName = FString("Android");
break;
case EPlatformCategoryType::IOS:
PlatformName = FString("IOS");
break;
case EPlatformCategoryType::Console:
PlatformName = FString("Console");
break;
}
return PlatformName;
}
FString FMaterialStatsUtils::ShaderPlatformTypeName(const EShaderPlatform PlatformID)
{
FString FormatName = LexToString(PlatformID);
if (FormatName.StartsWith(TEXT("SF_")))
{
FormatName.MidInline(3, MAX_int32, EAllowShrinking::No);
}
return FormatName;
}
FString FMaterialStatsUtils::GetPlatformOfflineCompilerPath(const EShaderPlatform ShaderPlatform)
{
FExtraShaderCompilerSettings SCSettings;
GetPlatformOfflineCompilerSettings(ShaderPlatform, SCSettings);
return SCSettings.OfflineCompilerPath;
}
void FMaterialStatsUtils::GetPlatformOfflineCompilerSettings(const EShaderPlatform ShaderPlatform, FExtraShaderCompilerSettings& SCSettings)
{
auto GetSCType = [](EOfflineShaderCompiler SC)
{
switch (SC)
{
case EOfflineShaderCompiler::Mali:
return EOfflineShaderCompilerType::Mali;
case EOfflineShaderCompiler::Adreno:
return EOfflineShaderCompilerType::Adreno;
default:
return EOfflineShaderCompilerType::Num;
}
};
if (FDataDrivenShaderPlatformInfo::GetNeedsOfflineCompiler(ShaderPlatform))
{
if (FDataDrivenShaderPlatformInfo::GetIsAndroidOpenGLES(ShaderPlatform)
|| (FDataDrivenShaderPlatformInfo::GetIsLanguageVulkan(ShaderPlatform) && FDataDrivenShaderPlatformInfo::GetIsMobile(ShaderPlatform)))
{
SCSettings.OfflineCompiler = GetSCType(GetDefault<UMaterialEditorSettings>()->OfflineCompiler);
SCSettings.OfflineCompilerPath = FPaths::ConvertRelativePathToFull(GetDefault<UMaterialEditorSettings>()->OfflineCompilerPath.FilePath);
SCSettings.GPUTarget = GetDefault<UMaterialEditorSettings>()->GPUTarget;
SCSettings.bDumpAll = GetDefault<UMaterialEditorSettings>()->bDumpAll;
SCSettings.bSaveCompilerStatsFiles = GetDefault<UMaterialEditorSettings>()->bSaveCompilerStatsFiles;
static const auto CVarMobileMultiView = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("vr.MobileMultiView"));
SCSettings.bMobileMultiView = (CVarMobileMultiView && CVarMobileMultiView->GetValueOnAnyThread() != 0);
return;
}
}
SCSettings.OfflineCompiler = EOfflineShaderCompilerType::Num;
return;
}
bool FMaterialStatsUtils::IsPlatformOfflineCompilerAvailable(const EShaderPlatform ShaderPlatform)
{
FExtraShaderCompilerSettings Settings;
GetPlatformOfflineCompilerSettings(ShaderPlatform, Settings);
bool bCompilerExists = FPaths::FileExists(Settings.OfflineCompilerPath);
return bCompilerExists;
}
bool FMaterialStatsUtils::PlatformNeedsOfflineCompiler(const EShaderPlatform ShaderPlatform)
{
return FDataDrivenShaderPlatformInfo::GetNeedsOfflineCompiler(ShaderPlatform);
}
FString FMaterialStatsUtils::RepresentativeShaderTypeToString(const ERepresentativeShader ShaderType)
{
switch (ShaderType)
{
case ERepresentativeShader::StationarySurface:
return TEXT("Stationary surface");
case ERepresentativeShader::StationarySurfaceCSM:
return TEXT("Stationary surface + CSM");
case ERepresentativeShader::StationarySurfaceNPointLights:
return TEXT("Stationary surface + Point Lights");
case ERepresentativeShader::DynamicallyLitObject:
return TEXT("Dynamically lit object");
case ERepresentativeShader::StaticMesh:
return TEXT("Static Mesh");
case ERepresentativeShader::SkeletalMesh:
return TEXT("Skeletal Mesh");
case ERepresentativeShader::SkinnedCloth:
return TEXT("Skinned Cloth");
case ERepresentativeShader::NaniteMesh:
return TEXT("Nanite Mesh");
case ERepresentativeShader::UIDefaultFragmentShader:
return TEXT("UI Pixel Shader");
case ERepresentativeShader::UIDefaultVertexShader:
return TEXT("UI Vertex Shader");
case ERepresentativeShader::UIInstancedVertexShader:
return TEXT("UI Instanced Vertex Shader");
case ERepresentativeShader::RuntimeVirtualTextureOutput:
return TEXT("Runtime Virtual Texture Output");
default:
return TEXT("Unknown shader name");
}
}
FSlateColor FMaterialStatsUtils::PlatformTypeColor(EPlatformCategoryType PlatformType)
{
FSlateColor Color(FStyleColors::Foreground);
switch (PlatformType)
{
case EPlatformCategoryType::Desktop:
Color = FStyleColors::AccentBlue;
break;
case EPlatformCategoryType::Android:
Color = FStyleColors::AccentGreen;
break;
case EPlatformCategoryType::IOS:
Color = FStyleColors::AccentYellow;
break;
case EPlatformCategoryType::Console:
Color = FStyleColors::AccentPurple;
break;
default:
return Color;
}
return Color;
}
FSlateColor FMaterialStatsUtils::QualitySettingColor(const EMaterialQualityLevel::Type QualityType)
{
switch (QualityType)
{
case EMaterialQualityLevel::Low:
return FStyleColors::AccentGreen;
case EMaterialQualityLevel::High:
return FStyleColors::AccentOrange;
case EMaterialQualityLevel::Medium:
return FStyleColors::Warning;
case EMaterialQualityLevel::Epic:
return FStyleColors::Error;
default:
return FStyleColors::Foreground;
}
}
void FMaterialStatsUtils::GetRepresentativeShaderTypesAndDescriptions(TMap<FName, TArray<FRepresentativeShaderInfo>>& ShaderTypeNamesAndDescriptions, const FMaterial* TargetMaterial)
{
static const FName FLocalVertexFactoryName = FLocalVertexFactory::StaticType.GetFName();
static const FName FGPUFactoryName = TEXT("TGPUSkinVertexFactoryDefault");
static const FName FClothVertexFactoryName = TEXT("TGPUSkinAPEXClothVertexFactoryDefault");
static const FName FNaniteVertexFactoryName = TEXT("FNaniteVertexFactory");
if (TargetMaterial->IsUIMaterial())
{
static FName TSlateMaterialShaderPSDefaultName = TEXT("TSlateMaterialShaderPSDefault");
ShaderTypeNamesAndDescriptions.FindOrAdd(FLocalVertexFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::UIDefaultFragmentShader, TSlateMaterialShaderPSDefaultName, TEXT("Default UI Pixel Shader")));
static FName TSlateMaterialShaderVSfalseName = TEXT("TSlateMaterialShaderVSfalse");
ShaderTypeNamesAndDescriptions.FindOrAdd(FLocalVertexFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::UIDefaultVertexShader, TSlateMaterialShaderVSfalseName, TEXT("Default UI Vertex Shader")));
static FName TSlateMaterialShaderVStrueName = TEXT("TSlateMaterialShaderVStrue");
ShaderTypeNamesAndDescriptions.FindOrAdd(FLocalVertexFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::UIInstancedVertexShader, TSlateMaterialShaderVStrueName, TEXT("Instanced UI Vertex Shader")));
}
else if (TargetMaterial->GetFeatureLevel() >= ERHIFeatureLevel::SM5)
{
if (TargetMaterial->GetShadingModels().IsUnlit())
{
//unlit materials are never lightmapped
static FName TBasePassPSFNoLightMapPolicyName = TEXT("TBasePassPSFNoLightMapPolicy");
ShaderTypeNamesAndDescriptions.FindOrAdd(FLocalVertexFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::StationarySurface, TBasePassPSFNoLightMapPolicyName, TEXT("Base pass shader without light map")));
}
else
{
//also show a dynamically lit shader
static FName TBasePassPSFNoLightMapPolicyName = TEXT("TBasePassPSFNoLightMapPolicy");
ShaderTypeNamesAndDescriptions.FindOrAdd(FLocalVertexFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::DynamicallyLitObject, TBasePassPSFNoLightMapPolicyName, TEXT("Base pass shader")));
if (IsStaticLightingAllowed())
{
if (TargetMaterial->IsUsedWithStaticLighting())
{
static FName TBasePassPSTLightMapPolicyName = TEXT("TBasePassPSTDistanceFieldShadowsAndLightMapPolicyHQ");
ShaderTypeNamesAndDescriptions.FindOrAdd(FLocalVertexFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::StationarySurface, TBasePassPSTLightMapPolicyName, TEXT("Base pass shader with Surface Lightmap")));
}
static FName TBasePassPSFPrecomputedVolumetricLightmapLightingPolicyName = TEXT("TBasePassPSFPrecomputedVolumetricLightmapLightingPolicy");
ShaderTypeNamesAndDescriptions.FindOrAdd(FLocalVertexFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::DynamicallyLitObject, TBasePassPSFPrecomputedVolumetricLightmapLightingPolicyName, TEXT("Base pass shader with Volumetric Lightmap")));
}
}
static FName TBasePassVSFNoLightMapPolicyName = TEXT("TBasePassVSFNoLightMapPolicy");
ShaderTypeNamesAndDescriptions.FindOrAdd(FLocalVertexFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::StaticMesh, TBasePassVSFNoLightMapPolicyName, TEXT("Base pass vertex shader")));
if (TargetMaterial->IsUsedWithSkeletalMesh() || TargetMaterial->IsUsedWithMorphTargets())
{
ShaderTypeNamesAndDescriptions.FindOrAdd(FGPUFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::SkeletalMesh, TBasePassVSFNoLightMapPolicyName, TEXT("Base pass vertex shader")));
}
if (TargetMaterial->IsUsedWithAPEXCloth())
{
ShaderTypeNamesAndDescriptions.FindOrAdd(FClothVertexFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::SkinnedCloth, TBasePassVSFNoLightMapPolicyName, TEXT("Base pass vertex shader")));
}
if (TargetMaterial->GetFeatureLevel() >= ERHIFeatureLevel::SM6)
{
if (TargetMaterial->IsUsedWithNanite())
{
static FName TBasePassCSFNoLightMapPolicyName = TEXT("TBasePassCSFNoLightMapPolicy");
ShaderTypeNamesAndDescriptions.FindOrAdd(FNaniteVertexFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::NaniteMesh, TBasePassCSFNoLightMapPolicyName, TEXT("Nanite Compute Shader")));
}
}
// Add the shader type with the most sampler usages so we can accurately report the worst case scenario.
// This is ad-hoc, and ideally we have a better way for finding this shader type in the future.
ShaderTypeNamesAndDescriptions.FindOrAdd(FLocalVertexFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::StationarySurface, FName(TEXT("TBasePassPSFCachedVolumeIndirectLightingPolicy")), TEXT("MaxSampler")));
// For materials that write to a runtime virtual texture add a pixel shader stat.
if (TargetMaterial->HasRuntimeVirtualTextureOutput())
{
static const FName LandscapeFactoryName = TEXT("FLandscapeVertexFactory");
static const FName RuntimeVirtualTexturePSName = TEXT("TVirtualTexturePSBaseColorNormalSpecular");
ShaderTypeNamesAndDescriptions.FindOrAdd(LandscapeFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::RuntimeVirtualTextureOutput, FName(RuntimeVirtualTexturePSName), TEXT("Runtime Virtual Texture Output")));
}
}
else
{
static const FName TMobileBasePassVSFNoLightMapPolicyName = TEXT("TMobileBasePassVSFNoLightMapPolicy");
static const FName TMobileBasePassPSFNoLightMapPolicyName = TEXT("TMobileBasePassPSFNoLightMapPolicyLOCAL_LIGHTS_DISABLED");
if (TargetMaterial->GetShadingModels().IsUnlit())
{
ShaderTypeNamesAndDescriptions.Add(FLocalVertexFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::StationarySurface, TMobileBasePassPSFNoLightMapPolicyName,
TEXT("Mobile base pass shader without light map")));
}
else
{
if (IsStaticLightingAllowed() && TargetMaterial->IsUsedWithStaticLighting())
{
static auto* CVarAllowDistanceFieldShadows = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Mobile.AllowDistanceFieldShadows"));
const bool bAllowDistanceFieldShadows = CVarAllowDistanceFieldShadows->GetValueOnAnyThread() != 0;
if (bAllowDistanceFieldShadows)
{
// distance field shadows only shaders
static const FName TMobileBasePassPSFMobileDistanceFieldShadowsAndLQLightMapPolicyName =
TEXT("TMobileBasePassPSFMobileDistanceFieldShadowsAndLQLightMapPolicyLOCAL_LIGHTS_DISABLED");
ShaderTypeNamesAndDescriptions.FindOrAdd(FLocalVertexFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::StationarySurface, TMobileBasePassPSFMobileDistanceFieldShadowsAndLQLightMapPolicyName,
TEXT("Mobile base pass shader with distance field shadows")));
}
else //no shadows & lightmapped
{
static const FName TMobileBasePassPSTLightMapPolicyLQName =
TEXT("TMobileBasePassPSTLightMapPolicyLQLOCAL_LIGHTS_DISABLED");
ShaderTypeNamesAndDescriptions.FindOrAdd(FLocalVertexFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::StationarySurface, TMobileBasePassPSTLightMapPolicyLQName,
TEXT("Mobile base pass shader with static lighting")));
}
}
// dynamically lit shader NoLightmapPolicy
ShaderTypeNamesAndDescriptions.FindOrAdd(FLocalVertexFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::DynamicallyLitObject, TMobileBasePassPSFNoLightMapPolicyName,
TEXT("Mobile base pass shader with only dynamic lighting")));
}
ShaderTypeNamesAndDescriptions.FindOrAdd(FLocalVertexFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::StaticMesh, TMobileBasePassVSFNoLightMapPolicyName,
TEXT("Mobile base pass vertex shader")));
if (TargetMaterial->IsUsedWithSkeletalMesh() || TargetMaterial->IsUsedWithMorphTargets())
{
ShaderTypeNamesAndDescriptions.FindOrAdd(FGPUFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::SkeletalMesh, TMobileBasePassVSFNoLightMapPolicyName,
TEXT("Mobile base pass vertex shader")));
}
if (TargetMaterial->IsUsedWithAPEXCloth())
{
ShaderTypeNamesAndDescriptions.FindOrAdd(FClothVertexFactoryName)
.Add(FRepresentativeShaderInfo(ERepresentativeShader::SkinnedCloth, TMobileBasePassVSFNoLightMapPolicyName,
TEXT("Mobile base pass vertex shader")));
}
}
}
#if WITH_EDITORONLY_DATA
static FString GetShaderString(const TArray<FGenericShaderStat>& Statistics)
{
TStringBuilder<2048> StatisticsStrBuilder;
for (const auto& Stat : Statistics)
{
StatisticsStrBuilder << Stat.StatName << ": ";
Visit([&StatisticsStrBuilder](auto& StoredValue)
{
StatisticsStrBuilder << StoredValue << "\n";
}, Stat.Value);
}
return StatisticsStrBuilder.ToString();
}
#endif // WITH_EDITORONLY_DATA
/**
* Gets instruction counts that best represent the likely usage of this material based on shading model and other factors.
* @param Results - an array of descriptions to be populated
*/
void FMaterialStatsUtils::GetRepresentativeInstructionCounts(TArray<FShaderInstructionsInfo>& Results, const FMaterialResource* Target)
{
TMap<FName, TArray<FRepresentativeShaderInfo>> ShaderTypeNamesAndDescriptions;
Results.Empty();
//when adding a shader type here be sure to update FPreviewMaterial::ShouldCache()
//so the shader type will get compiled with preview materials
const FMaterialShaderMap* MaterialShaderMap = Target->GetGameThreadShaderMap();
if (MaterialShaderMap)
{
GetRepresentativeShaderTypesAndDescriptions(ShaderTypeNamesAndDescriptions, Target);
TStaticArray<bool, (int32)ERepresentativeShader::Num> bShaderTypeAdded(InPlace, false);
if (Target->IsUIMaterial())
{
//for (const TPair<FName, FRepresentativeShaderInfo>& ShaderTypePair : ShaderTypeNamesAndDescriptions)
for (auto DescriptionPair : ShaderTypeNamesAndDescriptions)
{
auto& DescriptionArray = DescriptionPair.Value;
for (int32 i = 0; i < DescriptionArray.Num(); ++i)
{
const FRepresentativeShaderInfo& ShaderInfo = DescriptionArray[i];
if (!bShaderTypeAdded[(int32)ShaderInfo.ShaderType])
{
FShaderType* ShaderType = FindShaderTypeByName(ShaderInfo.ShaderName);
check(ShaderType);
const int32 NumInstructions = MaterialShaderMap->GetMaxNumInstructionsForShader(ShaderType);
FShaderInstructionsInfo Info;
Info.ShaderType = ShaderInfo.ShaderType;
Info.ShaderDescription = ShaderInfo.ShaderDescription;
Info.InstructionCount = NumInstructions;
Info.ShaderStatisticsString = GetShaderString(MaterialShaderMap->GetShaderStatistics(ShaderType));
if (Info.ShaderStatisticsString.Len() == 0)
{
Info.ShaderStatisticsString = TEXT("n/a");
}
Results.Push(Info);
bShaderTypeAdded[(int32)ShaderInfo.ShaderType] = true;
}
}
}
}
else
{
for (auto DescriptionPair : ShaderTypeNamesAndDescriptions)
{
FVertexFactoryType* FactoryType = FindVertexFactoryType(DescriptionPair.Key);
const FMeshMaterialShaderMap* MeshShaderMap = MaterialShaderMap->GetMeshShaderMap(FactoryType);
if (MeshShaderMap)
{
TMap<FHashedName, TShaderRef<FShader>> ShaderMap;
MeshShaderMap->GetShaderList(*MaterialShaderMap, ShaderMap);
auto& DescriptionArray = DescriptionPair.Value;
for (int32 i = 0; i < DescriptionArray.Num(); ++i)
{
const FRepresentativeShaderInfo& ShaderInfo = DescriptionArray[i];
if (!bShaderTypeAdded[(int32)ShaderInfo.ShaderType])
{
TShaderRef<FShader>* ShaderEntry = ShaderMap.Find(ShaderInfo.ShaderName);
if (ShaderEntry != nullptr)
{
FShaderType* ShaderType = (*ShaderEntry).GetType();
{
const int32 NumInstructions = MeshShaderMap->GetMaxNumInstructionsForShader(*MaterialShaderMap, ShaderType);
FShaderInstructionsInfo Info;
Info.ShaderType = ShaderInfo.ShaderType;
Info.ShaderDescription = ShaderInfo.ShaderDescription;
Info.InstructionCount = NumInstructions;
#if WITH_EDITORONLY_DATA
Info.ShaderStatisticsString = GetShaderString(MeshShaderMap->GetShaderStatistics(*MaterialShaderMap, ShaderType));
#endif // WITH_EDITORONLY_DATA
if (Info.ShaderStatisticsString.Len() == 0)
{
Info.ShaderStatisticsString = TEXT("n/a");
}
Results.Push(Info);
bShaderTypeAdded[(int32)ShaderInfo.ShaderType] = true;
}
}
}
}
}
}
}
}
}
void FMaterialStatsUtils::ExtractMatertialStatsInfo(EShaderPlatform ShaderPlatform, FShaderStatsInfo& OutInfo, const FMaterialResource* MaterialResource)
{
// extract potential errors
const ERHIFeatureLevel::Type MaterialFeatureLevel = MaterialResource->GetFeatureLevel();
FString FeatureLevelName;
GetFeatureLevelName(MaterialFeatureLevel, FeatureLevelName);
OutInfo.Empty();
TArray<FString> CompileErrors = MaterialResource->GetCompileErrors();
for (int32 ErrorIndex = 0; ErrorIndex < CompileErrors.Num(); ErrorIndex++)
{
OutInfo.StrShaderErrors += FString::Printf(TEXT("[%s] %s\n"), *FeatureLevelName, *CompileErrors[ErrorIndex]);
}
bool bNoErrors = OutInfo.StrShaderErrors.Len() == 0;
if (bNoErrors)
{
// extract instructions info
TArray<FMaterialStatsUtils::FShaderInstructionsInfo> ShaderInstructionInfo;
GetRepresentativeInstructionCounts(ShaderInstructionInfo, MaterialResource);
for (int32 InstructionIndex = 0; InstructionIndex < ShaderInstructionInfo.Num(); InstructionIndex++)
{
FShaderStatsInfo::FContent Content;
Content.StrDescription = ShaderInstructionInfo[InstructionIndex].InstructionCount > 0 ? FString::Printf(TEXT("%u"), ShaderInstructionInfo[InstructionIndex].InstructionCount) : TEXT("n/a");
Content.StrDescriptionLong = ShaderInstructionInfo[InstructionIndex].InstructionCount > 0 ?
FString::Printf(TEXT("%s: %u instructions\nStats: %s"), *ShaderInstructionInfo[InstructionIndex].ShaderDescription, ShaderInstructionInfo[InstructionIndex].InstructionCount, *ShaderInstructionInfo[InstructionIndex].ShaderStatisticsString) :
TEXT("Offline shader compiler not available or an error was encountered!");
OutInfo.ShaderInstructionCount.Add(ShaderInstructionInfo[InstructionIndex].ShaderType, Content);
FString Description = ShaderInstructionInfo[InstructionIndex].ShaderStatisticsString;
FShaderStatsInfo::FContent GenericContent;
GenericContent.StrDescription = Description;
GenericContent.StrDescriptionLong = Description;
OutInfo.GenericShaderStatistics.Add(ShaderInstructionInfo[InstructionIndex].ShaderType, GenericContent);
}
// 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);
OutInfo.TextureSampleCount.StrDescriptionLong = FString::Printf(TEXT("Texture Lookups (Est.): Vertex(%u), Pixel(%u)"), NumVSTextureSamples, NumPSTextureSamples);
// extract estimated VT info
const uint32 NumVirtualTextureLookups = MaterialResource->GetEstimatedNumVirtualTextureLookups();
OutInfo.VirtualTextureLookupCount.StrDescription = FString::Printf(TEXT("%u"), NumVirtualTextureLookups);
OutInfo.VirtualTextureLookupCount.StrDescriptionLong = FString::Printf(TEXT("Virtual Texture Lookups (Est.): %u"), NumVirtualTextureLookups);
// extract interpolators info
uint32 UVScalarsUsed, CustomInterpolatorScalarsUsed;
MaterialResource->GetUserInterpolatorUsage(UVScalarsUsed, CustomInterpolatorScalarsUsed);
const uint32 TotalScalars = UVScalarsUsed + CustomInterpolatorScalarsUsed;
const uint32 MaxScalars = FMath::DivideAndRoundUp(TotalScalars, 4u) * 4;
OutInfo.InterpolatorsCount.StrDescription = FString::Printf(TEXT("%u/%u"), TotalScalars, MaxScalars);
OutInfo.InterpolatorsCount.StrDescriptionLong = FString::Printf(TEXT("User interpolators: %u/%u Scalars (%u/4 Vectors) (TexCoords: %i, Custom: %i)"),
TotalScalars, MaxScalars, MaxScalars / 4, UVScalarsUsed, CustomInterpolatorScalarsUsed);
// Extract total shader count w/o having to compile shaders.
FPlatformTypeLayoutParameters LayoutParams;
LayoutParams.InitializeForPlatform(nullptr);
TArray<FDebugShaderTypeInfo> OutShaderInfo;
MaterialResource->GetShaderTypes(ShaderPlatform, LayoutParams, OutShaderInfo);
int TotalShadersForMaterial = 0;
for (const FDebugShaderTypeInfo& ShaderInfo : OutShaderInfo)
{
TotalShadersForMaterial += ShaderInfo.ShaderTypes.Num();
for (const FDebugShaderPipelineInfo& PipelineInfo : ShaderInfo.Pipelines)
{
TotalShadersForMaterial += PipelineInfo.ShaderTypes.Num();
}
}
OutInfo.ShaderCount.StrDescription = FString::Printf(TEXT("%u"), TotalShadersForMaterial);
OutInfo.ShaderCount.StrDescriptionLong = FString::Printf(TEXT("Total Shaders: %u"), TotalShadersForMaterial);
FString LWCMessage;
FMaterialResource::FLWCUsagesArray LWCFuncUsagesVS;
FMaterialResource::FLWCUsagesArray LWCFuncUsagesPS;
FMaterialResource::FLWCUsagesArray LWCFuncUsagesCS;
MaterialResource->GetEstimatedLWCFuncUsages(LWCFuncUsagesVS, LWCFuncUsagesPS, LWCFuncUsagesCS);
for (int KindIndex = 0; KindIndex < (int)ELWCFunctionKind::Max; ++KindIndex)
{
int Usages = LWCFuncUsagesVS[KindIndex] + LWCFuncUsagesPS[KindIndex] + LWCFuncUsagesCS[KindIndex];
if (Usages > 0)
{
LWCMessage += FString::Printf(
TEXT("%s: %u (VS), %u (PS), %u (CS)\n"),
*UEnum::GetDisplayValueAsText((ELWCFunctionKind)KindIndex).ToString(),
LWCFuncUsagesVS[KindIndex],
LWCFuncUsagesPS[KindIndex],
LWCFuncUsagesCS[KindIndex]);
}
}
OutInfo.LWCUsage.StrDescription = LWCMessage;
OutInfo.LWCUsage.StrDescriptionLong = LWCMessage;
if (FMaterialShaderMap* ShaderMap = MaterialResource->GetGameThreadShaderMap())
{
// Add number of preshaders and stats
uint32 TotalParams, TotalOps;
MaterialResource->GetPreshaderStats(TotalParams, TotalOps);
OutInfo.PreShaderCount.StrDescription = FString::Printf(TEXT("%u outputs\n%u params\n%u ops"), ShaderMap->GetNumPreshaders(), TotalParams, TotalOps);
OutInfo.PreShaderCount.StrDescriptionLong = FString::Printf(TEXT("%u outputs, %u parameter fetches, %u total operations"), ShaderMap->GetNumPreshaders(), TotalParams, TotalOps);
}
}
}
/*end FMaterialStatsUtils */
/***********************************************************************************************************************/