UE4 性能分级

3

主题

6

帖子

12

积分

新手上路

Rank: 1

积分
12
发表于 2023-3-12 07:57:28 | 显示全部楼层
虚幻引擎提供了性能分级的功能,主要是靠Scalability和Device Profile。这部分功能很完善,使用也没有什么技术难度,但是要用好还是有很多地方需要注意。
Scalability

Unreal中通过很多CVar来控制一些渲染特性的开启关闭和渲染质量,Scalability就是给这些CVar分了个类,让一个Quality影响多个CVar,方便控制,比如ShadowQuality就会影响这么多个跟阴影相关的CVar:
[ShadowQuality@2]
r.LightFunctionQuality=1
r.ShadowQuality=5
r.Shadow.CSM.MaxCascades=4
r.Shadow.MaxResolution=1024
r.Shadow.MaxCSMResolution=2048
r.Shadow.RadiusThreshold=0.04
r.Shadow.DistanceScale=0.85
r.Shadow.CSM.TransitionScale=0.8
r.Shadow.PreShadowResolutionFactor=0.5
r.DistanceFieldShadowing=1
r.DistanceFieldAO=1
r.AOQuality=1
r.VolumetricFog=1
r.VolumetricFog.GridPixelSize=16
r.VolumetricFog.GridSizeZ=64
r.VolumetricFog.HistoryMissSupersampleCount=4
r.LightMaxDrawDistanceScale=1
r.CapsuleShadows=1这些Scalability的配置都在这个文件里:BaseScalability.ini。
也有两个CVar没有包含在Scalability:

  • r.DetailMode
  • r.MaterialQualityLevel
自定义Scalability Settings

如果想自定义Scalability,可以在Project\Config目录新建DefaultScalability.ini文件,在这文件里填上想要的配置:


也可以在Project\Config\Android目录创建AndroidScalability.ini文件,这里可以放每个平台的Scalability:


这几个目录的优先级是这样,后面的会覆盖前面的配置:


如果想修改引擎的默认配置,比如让PostProcessQuality不再控制r.BloomQuality,而是让EffectsQuality控制r.BloomQuality,可以利用配置文件的-语法这样修改:
    [PostProcessQuality@0]
    -r.BloomQuality=4

    [PostProcessQuality@1]
    -r.BloomQuality=4

    [PostProcessQuality@2]
    -r.BloomQuality=5

    [PostProcessQuality@3]
    -r.BloomQuality=5

    [PostProcessQuality@Cine]
    -r.BloomQuality=5

    [EffectsQuality@0]
    r.BloomQuality=2

    [EffectsQuality@1]
    r.BloomQuality=2

    [EffectsQuality@2]
    r.BloomQuality=2

    [EffectsQuality@3]
    r.BloomQuality=5

    [EffectsQuality@Cine]
    r.BloomQuality=5实现原理

Scalability.ini文件中每一组Quality都对应了一个sg开头的CVar,比如ShadowQuality就对应的sg.ShadowQuality:
static TAutoConsoleVariable<int32> CVarShadowQuality(
   TEXT("sg.ShadowQuality"),
   Scalability::DefaultQualityLevel,
   TEXT("Scalability quality state (internally used by scalability system, ini load/save or using SCALABILITY console command)\n")
   TEXT(" 0:low, 1:med, 2:high, 3:epic, 4:cinematic, default: 3"),
   ECVF_ScalabilityGroup);
这些CVar都绑定了一个回调函数:
void OnChangeShadowQuality(IConsoleVariable* Var)
{
   SetGroupQualityLevel(TEXT("ShadowQuality"), Var->GetInt(), CVarShadowQuality_NumLevels->GetInt());
}
SetGroupQualityLevel函数中会找出ShadowQuality这个Section下的所有CVar,然后应用配置的值:
static void SetGroupQualityLevel(const TCHAR* InGroupName, int32 InQualityLevel, int32 InNumLevels)
{
// UE_LOG(LogConsoleResponse, Display, TEXT("  %s %d"), InGroupName, InQualityLevel);
   FString Section = GetScalabilitySectionString(InGroupName, InQualityLevel, InNumLevels);

#if WITH_EDITOR
   if (PlatformScalabilityName != NAME_None)
   {
      ApplyScalabilityGroupFromPlatformIni(*Section, *PlatformScalabilityIniFilename);
   }
   else
#endif
   {
      ApplyCVarSettingsFromIni(*Section, *GScalabilityIni, ECVF_SetByScalability);
   }
}

void ApplyCVarSettingsFromIni(const TCHAR* InSectionName, const TCHAR* InIniFilename, uint32 SetBy, bool bAllowCheating)
{
        FCoreDelegates::OnApplyCVarFromIni.Broadcast(InSectionName, InIniFilename, SetBy, bAllowCheating);

        UE_LOG(LogConfig,Log,TEXT("Applying CVar settings from Section [%s] File [%s]"),InSectionName,InIniFilename);

        if(FConfigSection* Section = GConfig->GetSectionPrivate(InSectionName, false, true, InIniFilename))
        {
                for(FConfigSectionMap::TConstIterator It(*Section); It; ++It)
                {
                        const FString& KeyString = It.Key().GetPlainNameString();
                        const FString& ValueString = It.Value().GetValue();

                        OnSetCVarFromIniEntry(InIniFilename, *KeyString, *ValueString, SetBy, bAllowCheating);
                }
        }
}
运行时修改Scalability

编辑器中修改:


通过控制台命令修改:


通过蓝图的GameUserSettings的接口修改:


通过C++的GameUserSettings的接口修改:
//Getting a reference to the Game User Settings.
UGameUserSettings* UserSettings = UGameUserSettings::GetGameUserSettings();

//Getting the current Foliage Quality.
Int32 FoliageQuality = UserSettings->GetFoliageQuality();

//Setting the current Foliage Quality to High.
UserSettings->SetFoliageQuality(2);
通过C++的Scalability的接口修改:
Scalability::FQualityLevels QualityLevel = Scalability::GetQualityLevels();
QualityLevel.SetFoliageQuality(2);
Scalability::SetQualityLevels(QualityLevel, true);
其他注意事项

ResolutionQuality不是0/1/2/3,它的值就是r.ScreenPercentage的值:
; Note: [ResolutionQuality] isn't using the usual 0/1/2/3 quality levels, the value directly maps to r.ScreenPercentage.
; This is because we want custom scaling aligned with the screen/window resolution with more fine grained control
Device Profiles

Device Profiles是Unreal提供的针对每个设备配置不同的参数的功能,可以在Window->Developer Tools->Device Profiles窗口编辑:


新建一个工程就有这么多个Device Profile,这是引擎默认定义的一些Device Profile,在Engine\Config\BaseDeviceProfiles.ini文件中,默认的这些Device Profile会缺少一些新设备,比如iPhone14。
每个Device Profile可以配置CVar、TextureGroup,而且Device Profile可以继承,比如引擎默认定义的就有Android_Low、Android_Mid、Android_High:


其他具体的设备继承这几个,然后再Override自己特有的配置。
配置CVar的窗口有一项是Scalability Group,这里都是sg开头的CVar:


每个Device Profile都可以配置这些Scalability的CVar,这样就把Scalability和Device Profiles这两个系统联系起来了,不同的设备配置不同的Scalability,Scalability又通过一组CVar控制渲染特性。
BaseDeviceProfiles.ini

下面总结一下BaseDeviceProfiles.ini这个文件都有哪些内容。
[DeviceProfiles]这个Section列出了所有的Device Profile,iOS是每个设备定义一个Device Profile,Android是根据GPU来定义Device Profile:
[DeviceProfiles]
+DeviceProfileNameAndTypes=Windows,Windows
+DeviceProfileNameAndTypes=WindowsNoEditor,Windows
+DeviceProfileNameAndTypes=WindowsServer,Windows
+DeviceProfileNameAndTypes=WindowsClient,Windows
+DeviceProfileNameAndTypes=IOS,IOS
+DeviceProfileNameAndTypes=iPadAir,IOS
+DeviceProfileNameAndTypes=iPadAir2,IOS
+DeviceProfileNameAndTypes=iPadAir3,IOS
......
+DeviceProfileNameAndTypes=Android,Android
+DeviceProfileNameAndTypes=Android_Low,Android
+DeviceProfileNameAndTypes=Android_Mid,Android
+DeviceProfileNameAndTypes=Android_High,Android
+DeviceProfileNameAndTypes=Android_Default,Android
+DeviceProfileNameAndTypes=Android_Adreno4xx,Android
+DeviceProfileNameAndTypes=Android_Adreno5xx_Low,Android
+DeviceProfileNameAndTypes=Android_Adreno5xx,Android
+DeviceProfileNameAndTypes=Android_Adreno6xx,Android
+DeviceProfileNameAndTypes=Android_Adreno6xx_Vulkan,Android
+DeviceProfileNameAndTypes=Android_Mali_T6xx,Android
......[/Script/Engine.TextureLODSettings]是TextureGroup的配置:
[/Script/Engine.TextureLODSettings]
@TextureLODGroups=Group
TextureLODGroups=(Group=TEXTUREGROUP_World,MinLODSize=1,MaxLODSize=16384,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_WorldNormalMap,MinLODSize=1,MaxLODSize=16384,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_WorldSpecular,MinLODSize=1,MaxLODSize=16384,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)
+TextureLODGroups=(Group=TEXTUREGROUP_Character,MinLODSize=1,MaxLODSize=16384,LODBias=0,MinMagFilter=aniso,MipFilter=point,MipGenSettings=TMGS_SimpleAverage)[IOSDeviceMappings]是iOS的id跟手机型号的映射,最后几行是没有匹配到的设备使用哪个Device Profile,比如当前没有iPhone14的Device Profile,那就会使用iPhone12的Device Profile:
[IOSDeviceMappings]
iPhone6,[1,2]=iPhone5S
iPhone7,1=iPhone6Plus
iPhone7,2=iPhone6
iPhone8,1=iPhone6S
iPhone8,2=iPhone6SPlus
iPhone8,4=iPhoneSE
......
; Below are generic fallbacks for unrecognized devices. Add any specific devices above this section.
iPhone=iPhone12
iPod=iPodTouch7
iPad=iPad8
AppleTV=AppleTV4K这个id是这样获取的:
FString GetIOSDeviceIDString()
{
   static FString CachedResult;
   static bool bCached = false;
   if (!bCached)
   {
      // get the device hardware type string length
      size_t DeviceIDLen;
      sysctlbyname("hw.machine", NULL, &DeviceIDLen, NULL, 0);

      // get the device hardware type
      char* DeviceID = (char*)malloc(DeviceIDLen);
      sysctlbyname("hw.machine", DeviceID, &DeviceIDLen, NULL, 0);

      CachedResult = ANSI_TO_TCHAR(DeviceID);
      bCached = true;

      free(DeviceID);
   }

   return CachedResult;
}
获取到的设备id跟手机型号不是一个东西,比如id是(iPhone6,1),对应的手机型号是iPhone5S,这个对应关系在这个网站上列出来了,不过这个网站也不是Apple官方的:
[/Script/AndroidDeviceProfileSelector.AndroidDeviceProfileMatchingRules]这个Section是Android设备的匹配规则,Android跟iOS不一样,iOS设备只需要一个id就能知道是哪个设备,Android设备五花八门,所以Unreal是通过GPU型号来选择不同的Device Profile的:
[/Script/AndroidDeviceProfileSelector.AndroidDeviceProfileMatchingRules]
MatchProfile=(Profile="Android_Adreno4xx",Match=((SourceType=SRC_GpuFamily,CompareType=CMP_Regex,MatchString="Adreno \\(TM\\) 4[0-9][0-9]")))
+MatchProfile=(Profile="Android_Adreno5xx_Low_Vulkan",Match=((SourceType=SRC_GpuFamily,CompareType=CMP_Regex,MatchString="Adreno \\(TM\\) 5[0-1][0-9]"),(SourceType=SRC_AndroidVersion, CompareType=CMP_Regex,MatchString="([0-9]+).*"),(SourceType=SRC_PreviousRegexMatch,CompareType=CMP_GreaterEqual,MatchString="10"),(SourceType=SRC_VulkanAvailable,CompareType=CMP_Equal,MatchString="true")))
+MatchProfile=(Profile="Android_Adreno5xx_Low",Match=((SourceType=SRC_GpuFamily,CompareType=CMP_Regex,MatchString="Adreno \\(TM\\) 5[0-1][0-9]")))
......游戏启动时,会从上到下依次看每条规则是否匹配,比如Adreno5xx_Low_Vulkan在Adreno5xx_Low上面,如果游戏是开启了Vulkan打的包,就会匹配到Adreno5xx_Low_Vulkan,如果把Adreno5xx_Low放在上面,即使游戏开启了Vulkan,也不会匹配到Adreno5xx_Low_Vulkan。
匹配规则的格式是这样:
+MatchProfile=(Profile="Profile_Name", Match=( ( Rule 1 ), ( Rule 2 ), (...) )每个Rule的格式是这样:
SourceType=[source string], CompareType=[comparison type], MatchString=[string to compare the source string to]Android这边如果有设备没有匹配到具体的Device Profile就会使用Android_Default:


这里的逻辑是在代码里处理的:
struct CORE_API FAndroidMisc : public FGenericPlatformMisc
{
......
        static const TCHAR* GetDefaultDeviceProfileName() { return TEXT("Android_Default"); }
......
}
自定义Device Profile

想要自定义一个项目的Device Profile,需要点击Device Profiles窗口的Save as Default:


这样会在Project/Config目录创建DefaultDeviceProfiles.ini文件,然后修改这个文件的内容即可。注意在这个窗口编辑了DeviceProfile后,DefaultDeviceProfiles.ini不会自动保存,每次修改完成后都需要点击Save as Default才能保存。
不过UE4生成的DefaultDeviceProfiles.ini好像有问题,即使没有做任何修改,生成的文件里也会有所有的Device Profile,而且每个Device Profile下面都有一堆TextureLODGroup的配置,导致这个文件内容很多,不利于修改和阅读:


如果想要自定义TextureLODGroup,最好是在DefaultDeviceProfiles.ini中添加 [/Script/Engine.TextureLODSettings],而不是每个Device Profile都配置一次TextureLODGroup。
UE5.1生成的DefaultDeviceProfiles.ini就合理多了,如果只修改了Windows Device Profile,那这个文件的内容只会有Windows Device Profile:


查了查发现是UE5修改了保存Device Profile功能,不会保存没有修改的Profile,也会跳过继承的Texture Group:
https://github.com/EpicGames/UnrealEngine/commit/a46686befb127d79a72243c1857e2dd254017076


在DefaultDeviceProfiles.ini中也可以新增Device Profile,比如Lyra中就新增了这几个:
[DeviceProfiles]
; Add a new mobile type as a base for IOS and Android and several performance buckets
+DeviceProfileNameAndTypes=Mobile,Mobile
+DeviceProfileNameAndTypes=IOS_Low,IOS
+DeviceProfileNameAndTypes=IOS_Mid,IOS
+DeviceProfileNameAndTypes=IOS_High,IOS
+DeviceProfileNameAndTypes=IOS_Epic,IOS对于同一个Device Profile,DefaultDeviceProfiles.ini可以覆盖BaseDeviceProfiles.ini中的配置。具体规则以iPhone6S为例说明。
Lyra中iPhone6S有这几个配置:


DefaultDeviceProfiles.ini只配置了两个CVar:
[iPhone6S DeviceProfile]
BaseProfileName=IOS_Low
+CVars=r.MobileContentScaleFactor=1.5
+CVars=sg.ResolutionQuality=70剩下的都是BaseDeviceProfiles.ini中配置的:
[iPhone6S DeviceProfile]
DeviceType=IOS
BaseProfileName=IOS
+CVars=ios.PhysicalScreenDensity=326
+CVars=r.MobileContentScaleFactor=2
+CVars=sg.PostProcessQuality=3
+CVars=sg.ShadowQuality=2
+CVars=r.Mobile.AmbientOcclusionQuality=0
+CVars=r.Mobile.PixelProjectedReflectionQuality=0在Device Profile编辑窗口看到的CVar是两个文件配置的CVar的组合,不会因为在DefaultDeviceProfiles.ini定义了iPhone6s,而导致BaseDeviceProfiles.ini中iPhone6s配置的CVar无效。
继承的Device Profile的CVar在这里显示:


如果想去掉BaseDeviceProfiles.ini中引擎默认配置的CVar,有两种方法。第一种是通过Device Profiles窗口删除这些CVar,这样会在DefaultDeviceProfiles.ini中新增几行 -CVars= 开头的配置。第二种是直接加一行这个:
!Cvars=ClearArray但是TextureLODGroups不需要用-来取消之前的配置,这是因为在BaseDeviceProfiles.ini有这么一行:
; Add a ArrayOfStruct key for all DeviceProfile PerObjectConfig sections (this must come before any DeviceProfile sections)
; Note that * properties don't get written back out by the config system, but these aren't real sections, so that should be okay
[DeviceProfile]
*TextureLODGroups=Group加上这么一行后,就会把这个数组当成一个类似Map的东东,后面想修改配置的时候,只需要用相同的Key即可,文档中也有说明:


这里还需要注意,DefaultDeviceProfile.ini中指定的iPhone6S的BaseProfileName是IOS_Low,BaseDeviceProfile.ini中指定的IOS,最终的结果是DefaultDeviceProfile,ini覆盖了BaseDeviceProfile.ini。
初始化流程

每次游戏启动都会初始化Device Profile,流程如下:
int32 FEngineLoop::PreInit(const TCHAR* CmdLine)
{
        const int32 rv1 = PreInitPreStartupScreen(CmdLine);
        ......
}

int32 FEngineLoop::PreInitPreStartupScreen(const TCHAR* CmdLine)
{
    ......
            {
                        SCOPED_BOOT_TIMING("InitializeCVarsForActiveDeviceProfile");
                        // Set all CVars which have been setup in the device profiles.
                        // This may include scalability group settings which will override
                        // the defaults set above which can then be replaced below when
                        // the game user settings are loaded and applied.
                        UDeviceProfileManager::InitializeCVarsForActiveDeviceProfile();
                }
    ......
}
相关处理都在UDeviceProfileManager::InitializeCVarsForActiveDeviceProfile()这个函数里,这里会选择Device Profile,然后初始化这个Profile和它的Parent Tree的CVar。
首先会选择设备匹配到的Device Profile,是在这里处理的:
const FString UDeviceProfileManager::GetPlatformDeviceProfileName()
{
......
    FString DeviceProfileSelectionModule;
    if (GConfig->GetString(TEXT("DeviceProfileManager"), TEXT("DeviceProfileSelectionModule"), DeviceProfileSelectionModule, GEngineIni))
    {
        if (IDeviceProfileSelectorModule* DPSelectorModule = FModuleManager::LoadModulePtr<IDeviceProfileSelectorModule>(*DeviceProfileSelectionModule))
        {
            ActiveProfileName = DPSelectorModule->GetRuntimeDeviceProfileName();
        }
    }
......
}
不同的平台会根据配置文件中的DeviceProfileSelectionModule执行不同的代码,不同的Module会实现自己的GetRuntimeDeviceProfileName函数,比如Android就是AndroidDeviceProfileSelectorRuntime:


找到合适的Profile后,就会应用这个Profile和它的Parent Tree配置的CVar:
void UDeviceProfileManager::InitializeCVarsForActiveDeviceProfile(bool bPushSettings, bool bIsDeviceProfilePreview, bool bForceReload)
{
    ......
        while( bReachedEndOfTree == false )
        {
        ......应用CVar......
               
                        // Get the next device profile name, to look for CVars in, along the tree
                        FString NextBaseDeviceProfileName;
                        if( GConfig->GetString( *CurrentSectionName, TEXT("BaseProfileName"), NextBaseDeviceProfileName, GDeviceProfilesIni ) )
                        {
                                BaseDeviceProfileName = NextBaseDeviceProfileName;
                                UE_LOG(LogInit, Log, TEXT("Going up to parent DeviceProfile [%s]"), *BaseDeviceProfileName);
                        }
                        else
                        {
                                BaseDeviceProfileName.Empty();
                        }
                               
                // Check if we have inevitably reached the end of the device profile tree.
                bReachedEndOfTree = !bProfileExists || BaseDeviceProfileName.IsEmpty();
        }
}
有一个CVar可以Override Device Profile,可以用来调试每个Profile的配置,这个CVar设置了回调,可以运行时修改:
static TAutoConsoleVariable<FString> CVarDeviceProfileOverride(
   TEXT("dp.Override"),
   TEXT(""),
   TEXT("DeviceProfile override - setting this will use the named DP as the active DP. In addition, it will restore any\n")
   TEXT(" previous overrides before setting (does a dp.OverridePop before setting after the first time).\n")
   TEXT(" The commandline -dp option will override this on startup, but not when setting this at runtime\n"),
   ECVF_Default);
CVar SetBy Mask

EConsoleVariableFlags包括了一些SetByMask,这表示一个CVar被赋值的优先级,从上到下越来越高:
enum EConsoleVariableFlags
{
  ......
     /* to get some history of where the last value was set by ( useful for track down why a cvar is in a specific state */
   ECVF_SetByMask =            0xff000000,

   // the ECVF_SetBy are sorted in override order (weak to strong), the value is not serialized, it only affects it's override behavior when calling Set()

   // lowest priority (default after console variable creation)
   ECVF_SetByConstructor =          0x00000000,
   // from Scalability.ini (lower priority than game settings so it's easier to override partially)
   ECVF_SetByScalability =          0x01000000,
   // (in game UI or from file)
   ECVF_SetByGameSetting =          0x02000000,
   // project settings (editor UI or from file, higher priority than game setting to allow to enforce some setting fro this project)
   ECVF_SetByProjectSetting =    0x03000000,
   // per project setting (ini file e.g. Engine.ini or Game.ini)
   ECVF_SetBySystemSettingsIni =  0x04000000,
   // per device setting (e.g. specific iOS device, higher priority than per project to do device specific settings)
   ECVF_SetByDeviceProfile =     0x05000000,
   // consolevariables.ini (for multiple projects)
   ECVF_SetByConsoleVariablesIni = 0x06000000,
   // a minus command e.g. -VSync (very high priority to enforce the setting for the application)
   ECVF_SetByCommandline =          0x07000000,
   // least useful, likely a hack, maybe better to find the correct SetBy...
   ECVF_SetByCode =            0x08000000,
   // editor UI or console in game or editor
   ECVF_SetByConsole =             0x09000000,
};
举个例子说明作用,ECVF_SetByProjectSetting比ECVF_SetByScalability优先级高,如果在Scalability.ini的PostProcessQuality中配置了r.Mobile.PixelProjectedReflectionQuality,同时也在DefaultEngine.ini中配置了它,这样如果在运行时修改PostProcessQuality就不能修改r.Mobile.PixelProjectedReflectionQuality的值,因为它的优先级低,引擎还会报这样的警告:
LogConsoleManager: Warning: Setting the console variable 'r.Mobile.PixelProjectedReflectionQuality' with 'SetByScalability' was ignored as it is lower priority than the previous 'SetByProjectSetting'. Value remains '1'
所以就需要注意,在Device Profile和Scalability控制的CVar不要在其他优先级更高的地方修改它们的值
最佳应用

下面总结一下如何使用这部分功能使性能分级的管理更清晰、不容易出问题。
Scalability

我们应该在Project/Config/DefaultScalability.ini中配置所有平台相同的参数,在Project/Config/iOS/IOSScalability.ini配置iOS平台的参数,在Project/Config/Android/AndroidScalability.ini配置Android平台的参数。
注意:引擎默认配置了AndroidScalability.ini和iOSScalability.ini,重写了一些配置,而且两个文件有的配置是不一样的,最好是改成相同的配置,避免出现问题。


Device Profile

注意:

  • UE4的Device Profile编辑窗口的保存功能有问题,会保存没有修改的Profile和继承的TextureLODGroup,不方便阅读和修改,所以Device Profile只手动编辑,不通过编辑器修改。
  • 跟性能分级相关的配置放在Device Profile中,其他通用的配置放在其他配置文件。
Device Profile的继承关系这样划分比较清晰:


Mobile中放移动端共有的配置,Android、iOS分别放两个平台的配置,两组Low、Mid、High、Epic对应性能分级的4个等级。Android根据GPU型号分为不同的Device Profile,继承4个等级的其中一个,比如Android_Mali_G78继承Android_Epic。iOS根据具体的机型分为不同的Device Profile,比如iPhone12继承iOS_Epic,iPad9继承iOS_High。
由Scalability控制的设置配置在Low、Mid、High、Epic这些Profile中,比如Android_Low的抗锯齿质量应该是低,就这样配置:
[Android_Low DeviceProfile]
BaseProfileName=Android
+CVars=sg.AntiAliasingQuality=0
......性能分级中有的设置对应一个CVar,这些CVar直接配置在这4个等级的Device Profile中,有以下几个:

  • r.MaterialQualityLevel
  • r.DetailMode
注意r.MaterialQualityLevel的0、1、2、3对应的是low、high、medium、epic,high和medium是反的
static TAutoConsoleVariable<int32> CVarMaterialQualityLevel(
   TEXT("r.MaterialQualityLevel"),
   1,
   TEXT("0 corresponds to low quality materials, as defined by quality switches in materials, 1 corresponds to high, 2 for medium, and 3 for Epic."),
   ECVF_Scalability | ECVF_RenderThreadSafe);
还有Scalability的ShadingQuality不是控制的MaterialQualityLevel,默认是控制的几个HairStrands的参数:
[ShadingQuality@0]
r.HairStrands.SkyLighting.IntegrationType=2
r.HairStrands.SkyAO.SampleCount=4
r.HairStrands.Visibility.MSAA.SamplePerPixel=1
r.HairStrands.Interpolation.UseSingleGuide=1比如Android_Low的材质质量是低,应该这样配置:
[Android_Low DeviceProfile]
BaseProfileName=Android
+CVars=r.MaterialQualityLevel=0
......另外每个项目还需要根据性能分级做些不同的处理,这类设置可以在项目代码中新增对应的CVar,然后根据Device Profile配置的CVar的值做不同的处理,比如帧数设置:
static TAutoConsoleVariable<int32> CVarProjectDeviceProfileMaxFrame(
   TEXT("Project.DeviceProfile.MaxFrame"),
   60,
   TEXT("Max FPS.30 60"),
   ECVF_Default);
以下是DefaultDeviceProfile.ini的内容。
新增Device Profile

新增以下几个Device Profile:
[DeviceProfiles]
+DeviceProfileNameAndTypes=Mobile,Mobile
+DeviceProfileNameAndTypes=Android_Epic,Android
+DeviceProfileNameAndTypes=IOS_Low,IOS
+DeviceProfileNameAndTypes=IOS_Mid,IOS
+DeviceProfileNameAndTypes=IOS_High,IOS
+DeviceProfileNameAndTypes=IOS_Epic,IOS新增了实现分级功能的Profile,Mobile放通用配置,Low、Mid、High、Epic放分级配置,Android只需要增加一个Android_Epic,其他默认定义在BaseDeviceProfiles.ini。
如果有需要也可以加上一些新设备的Profile。
Mobile DeviceProfile

所有的DeviceProfile都做这两点处理:

  • 所有的Profile都加上!Cvars=ClearArray,目的是避免BaseDeviceProfile.ini配置的东西产生意料之外的作用,所有的配置都放在DefaultDeviceProfiles.ini,这样只需要关注这一个文件
  • 一些BaseDeviceProfiles.ini中需要的通用配置放到DefaultDeviceProfiles.ini里
[Mobile DeviceProfile]
DeviceType=Mobile
BaseProfileName=
!Cvars=ClearArray

; Begin - BaseScalability
+CVars=slate.AbsoluteIndices=1
; PF_B8G8R8A8
+CVars=r.DefaultBackBufferPixelFormat=0
; PreviewAllowlistCVars and PreviewDenyListCVars are arrays of cvars that are included or excluded from being applied in mobile preview.
; If any PreviewAllowlistCVars is set, cvars are denied by default.
PreviewAllowlistCVars=None
; End - BaseScalability

  • 因为Mobile没有继承的Profile,所以BaseProfileName=后面没有东东
  • Begin - BaseScalability和End - BaseScalability之间的就是原本配置在BaseScalability.ini中的东东
Android DeviceProfile

Android DeviceProfile配置了几个Vulkan的CVar:
[Android DeviceProfile]
DeviceType=Android
BaseProfileName=Mobile
!Cvars=ClearArray

; Begin - BaseScalability
+CVars=r.Vulkan.DelayAcquireBackBuffer=2
+CVars=r.Vulkan.RobustBufferAccess=1
+CVars=r.Vulkan.DescriptorSetLayoutMode=2
; Don't enable Vulkan by default. Specific device profiles can set this cvar to 0 to enable Vulkan.
+CVars=r.Android.DisableVulkanSupport=1
+CVars=r.Android.DisableVulkanSM5Support=1
; End - BaseScalabilityIOS DeviceProfile

iOS DeviceProfile配置了这几个CVar,还不清楚Android需不需要:
[IOS DeviceProfile]
DeviceType=IOS
BaseProfileName=Mobile
!Cvars=ClearArray

; Begin - BaseScalability
; HZB will be slower with tiled, and not needed
+CVars=r.HZBOcclusion=0
; Non need for depth prepass
+CVars=r.EarlyZPass=0
; Needs geometry shader support
+CVars=r.TranslucentLightingVolume=0
; Needs geometry shader support
+CVars=r.AllowPointLightCubemapShadows=0
; Not sure on this one - may need the whole D Buffer stuff going
+CVars=r.Decal.StencilSizeThreshold=-1
+CVars=r.MorphTarget.Mode=0
; End - BaseScalabilityIOSDeviceMappings

如果新增了iOS的新设备,还需要在IOSDeviceMappings中加上对应的ID:
[IOSDeviceMappings]
iPhone14,2=iPhone13Pro
iPhone14,3=iPhone13ProMax
iPhone14,4=iPhone13Mini增加新设备跟ID的映射,每个设备的ID可以在这个网站查:
最好把没有识别成功的设备也改成最新的设备:
-iPhone=iPhone12
-iPod=iPodTouch7
-iPad=iPad8
-AppleTV=AppleTV4K
iPhone=iPhone14
iPod=iPodTouch7
iPad=iPad10
AppleTV=AppleTV2_4K性能分级Profile

IOS_Low、IOS_Mid、IOS_High、IOS_Epic和Android_Low、Android_Mid、Andoird_High、Android_Epic这些Profile只配置跟性能分级相关的东东,包括三部分:

  • Scalability是由Scalability控制的CVar;
  • Engine CVar是控制的引擎中就有的CVar;
  • Project CVar是项目中自定义的CVar,这几个CVar需要在项目代码中根据CVar的值做不同的处理
示例如下:
[IOS_Low DeviceProfile]
BaseProfileName=IOS
!Cvars=ClearArray
; Scalability
+CVars=sg.AntiAliasingQuality=0
+CVars=sg.ShadowQuality=0
+CVars=sg.PostProcessQuality=0
+CVars=sg.TextureQuality=0
; Engine CVar
+CVars=r.MaterialQualityLevel=0
; Project CVar
+CVars=Project.DeviceProfile.MaxFrame=30Default Profile

iOS和Android的Default Profile都设为High,这是因为只有新设备会Fallback到Default Profile,旧设备都能匹配到自己的Profile,新设备的性能肯定是比旧设备好的。iOS是在Device Mapping配置的:
[IOSDeviceMappings]
......
; 以下是没有识别到的设备的fallback
-iPhone=iPhone12
-iPod=iPodTouch7
-iPad=iPad8
-AppleTV=AppleTV4K
iPhone=iPhone14
iPod=iPodTouch7
iPad=iPad10
AppleTV=AppleTV2_4KAndroid是在Android_Default DeviceProfile中配置的:
[Android_Default DeviceProfile]
DeviceType=Android
BaseProfileName=Android_EpicIOS Final DeviceProfile

iOS对每个机型配置一个Profile。每个Profile继承Low、Medium、High、Epic其中一个,同时还有一些iOS设备特有的配置。一个例子如下:
[iPhone11 DeviceProfile]
DeviceType=IOS
BaseProfileName=IOS_High
!Cvars=ClearArray
+CVars=ios.PhysicalScreenDensity=326
+CVars=r.MobileContentScaleFactor=2iOS设备的处理器可以在这个网站查:https://iosref.com/ram-processor。
每个Profile都需要配置跟分辨率相关的两个CVar:r.MobileContentScaleFactor和ios.PhysicalScreenDensity。这两个参数都可以在这个网站查到:https://www.ios-resolution.com/,r.MobileContentScaleFactor对应表格里的Scale Factor,ios.PhysicalScreenDensity对应PPI(Pixels Per Inch)。
r.MobileContentScaleFactor在iOS上就是苹果的scale factor。
ios.PhysicalScreenDensity最终会影响DragTriggerDistance,意思是手指头移动超过这个距离会认为是一次Drag:
void FSlateApplication::SetupPhysicalSensitivities()
{
   const float DragTriggerDistanceInInches = FUnitConversion::Convert(1.0f, EUnit::Millimeters, EUnit::Inches);
   FPlatformApplicationMisc::ConvertInchesToPixels(DragTriggerDistanceInInches, DragTriggerDistance);

   // TODO Rather than allow people to request the DragTriggerDistance directly, we should
   // probably store a drag trigger distance for touch and mouse, and force users to pass
   // the pointer event they're checking for, and if that pointer event is touch, use touch
   // and if not touch, return mouse.
#if PLATFORM_DESKTOP
   DragTriggerDistance = FMath::Max(DragTriggerDistance, 5.0f);
#else
   DragTriggerDistance = FMath::Max(DragTriggerDistance, 10.0f);
#endif

   FGestureDetector::LongPressAllowedMovement = DragTriggerDistance;
}
BaseDeviceProfiles中全面屏的iPhone都有这个:r.CustomUnsafeZones="(L:free[0,-15][812,15]);(P:fixed[83,0][206,30])",这个跟UI的适配有关:https://docs.unrealengine.com/4.27/en-US/InteractiveExperiences/UMG/UserGuide/UMGSafeZones/。不过好像只有Editor的代码会用到,暂时不填这个.
BaseDeviceProfiles的iPadPro多了好几个CVar,但是都没有什么用:
[iPadPro DeviceProfile]
DeviceType=IOS
BaseProfileName=IOS
+CVars=ios.PhysicalScreenDensity=264
+CVars=r.MobileContentScaleFactor=1.5
+CVars=g.TimeoutForBlockOnRenderFence=3000000
+CVars=r.Decal.StencilSizeThreshold=0.1
+CVars=r.MetalComputeParameterSize=1024
+CVars=r.EarlyZPass=3
+CVars=sg.ShadowQuality=3
+CVars=sg.PostProcessQuality=3
+CVars=sg.AntiAliasingQuality=3Android Final DeviceProfile

Android以GPU型号划分Profile,继承自Low、Medium、High、Epic其中一个,比如Adreno就是这样:
[Android_Adreno4xx DeviceProfile]
DeviceType=Android
BaseProfileName=Android_Low
!Cvars=ClearArray

[Android_Adreno5xx_Low DeviceProfile]
DeviceType=Android
BaseProfileName=Android_Low
!Cvars=ClearArray

[Android_Adreno5xx DeviceProfile]
DeviceType=Android
BaseProfileName=Android_Mid
!Cvars=ClearArray

[Android_Adreno6xx DeviceProfile]
DeviceType=Android
BaseProfileName=Android_High
!Cvars=ClearArray这是引擎的默认配置,每个项目需要根据测试的结果进行调整。可能需要细分,比如Adreno620跟Adreno660可能就差了一个级别。
有的Mali设备有bug,还加了两个处理bug的CVar,这是在BaseScalability.ini中配置的,也需要抄过来:
[Android_Mali_T6xx DeviceProfile]
DeviceType=Android
BaseProfileName=Android_Low
!Cvars=ClearArray
+CVars=r.Android.MaliMidgardIndexingBug=1

[Android_Mali_T7xx DeviceProfile]
DeviceType=Android
BaseProfileName=Android_Low
!Cvars=ClearArray
+CVars=r.Android.MaliMidgardIndexingBug=1

[Android_Mali_T8xx DeviceProfile]
DeviceType=Android
BaseProfileName=Android_Mid
!Cvars=ClearArray
+CVars=r.Android.MaliMidgardIndexingBug=1
; using early_fragment_tests in a fragment shader does not work correctly on this device
+CVars=r.Android.DisableEarlyFragmentTests=1Reference

Scalability:https://docs.unrealengine.com/4.26/en-US/TestingAndOptimization/PerformanceAndProfiling/Scalability/ScalabilityReference/
Device Profiles:https://docs.unrealengine.com/4.27/en-US/SharingAndReleasing/DeviceProfiles/
Config Files:https://docs.unrealengine.com/4.27/en-US/ProductionPipelines/ConfigurationFiles/
Customizing Device Profiles and Scalability for Android:https://docs.unrealengine.com/5.0/en-US/customizing-device-profiles-and-scalability-in-unreal-engine-projects-for-android/
Lyra Scalability and Device Profiles:https://docs.unrealengine.com/5.0/en-US/scalability-and-device-profiles-in-lyra-sample-game-for-unreal-engine/
Lyra Game Settings:https://docs.unrealengine.com/5.0/en-US/lyra-sample-game-settings-in-unreal-engine/

Mobile GPU评分:https://www.techcenturion.com/mobile-gpu-rankings
iOS分辨率:https://www.ios-resolution.com/
iOS ID:https://www.theiphonewiki.com/wiki/Models
iOS处理器:https://iosref.com/ram-processor
回复

举报 使用道具

您需要登录后才可以回帖 登录 | 立即注册
快速回复 返回顶部 返回列表