Unreal Engine* 4.19 的 CPU 功能检测

Unreal Engine* 4.19 发布之后,许多特性针对多核处理器而优化。过去,游戏引擎在图形特性和性能方面采用控制台设计点。一般而言,多数游戏未针对处理器进行优化,这使得大量 PC 性能得不到充分发挥。英特尔与 Unreal Engine 4 的合作重点是尽快帮助使用引擎的开发人员释放游戏的潜能,以充分利用 PC 平台的所有处理器计算能力。

英特尔对 Unreal Engine* 4.19 的支持工作取得了以下成效:

  • 增加了匹配用户处理器的工作线程的数量
  • 提高了布料物理系统的吞吐量
  • 集成了对英特尔® VTune™ 放大器的支持

为了充分利用高端 CPU 的额外计算能力,英特尔开发了一款能提供详细 CPU 指标与 SynthBenchmark 性能指标的插件。CPU 功能可以使用该插件的指标来划分特性与内容。通过这种方式划分特性与内容将支持您的游戏运行于各种系统,并且不会对整体性能造成影响。

如欲下载插件,请访问 GitHub Unreal 功能检测页面。

Unreal Engine* 4.19 功能检测插件

借助功能检测插件,您可以访问兼容 C++ 和蓝图的帮助函数(面向 CPU)、渲染硬件接口 (RHI) 函数和面向 CPU/GPU 的 SynthBenchmark 性能索引。

表 1.CPU 检测函数

第三方函数蓝图函数描述
Intel_IsIntelCPU()IsIntelCPU()如果是英特尔 CPU,返回 TRUE
Intel_GetNumLogicalCores()GetNumLogicalCores()返回逻辑内核数量
Intel_GetNumPhysicalCores()GetNumPhysicalCores()返回物理内核数量
Intel_GetCoreFrequency()GetCoreFrequency()返回当前的内核频率
Intel_GetMaxBaseFrequency()GetMaxBaseFrequency()返回最大内核频率
Intel_GetCorePercMaxFrequency()GetCorePercMaxFrequency()返回正在使用的最大内核频率 %
Intel_GetFullProcessorName()GetFullProcessorName()返回长处理器名称
Intel_GetProcessorName()GetProcessorName()返回短处理器名称
Intel_GetSKU()不适用未使用

表 2.高速缓存与内存检测函数

高速缓存与 Memory 函数
第三方函数蓝图函数描述
Intel_GetUsablePhysMemoryGB()GetUsablePhysMemoryGB()返回可用的物理内存(GB)
Intel_GetComittedMemoryMB()GetComittedMemoryMB()返回占用的内存(MB)
Intel_GetAvailableMemoryMB()GetAvailableMemoryMB()返回可用的内存(MB)

表 3.渲染硬件接口 (RHI) 包装程序函数

RHI 包装程序函数
第三方函数蓝图函数描述
不适用IsRHIIntel()如果是英特尔 GPU,返回 TRUE
不适用IsRHINVIDIA()如果是 NVIDIA GPU,返回 TRUE
不适用IsRHIAMD()如果是 AMD GPU,返回 TRUE
不适用RHIVendorName()返回 GPU 的厂商名称

表 4.SynthBenchmark 包装程序函数

SynthBenchmark 包装程序函数
第三方函数蓝图函数描述
不适用ComputeCPUPerfIndex()100:一般、良好的 CPU, <100:较慢, >100:较快
不适用ComputeGPUPerfIndex()100:一般、良好的 GPU, <100:较慢, >100:较快

SynthBenchmark

使用 SythBenchmark 包装程序时需要注意,首次调用每个 ComputeCPUPerfIndex()ComputeGPUPerfIndex() 都将产生少量的性能成本,同时计算性能索引。ComputeCPUPerfIndex()ComputeGPUPerfIndex() 的首次和后续调用如果在运行基准性能测试时没有产生额外的开销,性能索引值将被缓存。对于游戏中依赖性能的部分,建议您在启动或加载界面的过程中调用这两个函数。

安装功能检测插件

1.从 GitHub* 下载功能检测插件并打开项目文件夹。

Project folder caption

2.如果插件文件夹不在根目录中,请立即添加。

Steps to create the plugin folder

Plugin folder image

3.将功能检测插件提取至插件文件夹。

CapabilityDetect plugin folder

4.使用 .uproject 文件夹启动项目。

Steps to launch the project

5.访问主菜单中的 Edit > Plugins。加载插件窗口时,功能检测插件应安装在项目中。

Installed Capability Detect Plugin

现在,插件已安装完成,可以用它划分游戏内容与特性。下一节,我们将介绍 CPU 功能如何使用该插件划分特性。

Unreal Engine 4.19 特性划分

检测功能

为了借助平台配置划分特性,创建一个全新的 UDataAsset,并将其命名为 UPlatformConfigUPlatformConfig 将存储目标平台的特征,如物理内核数量、逻辑内核数量、可用的物理内存、处理器名称和/或 SynthBenchmark 性能索引。

#include "CoreMinimal.h"
#include "Engine/DataAsset.h"
#include "PlatformConfig.generated.h"
/**
 * Platform Configuration Data Asset
 */
UCLASS(BlueprintType)
class CAPABILITYDETECTDEMO_API UPlatformConfig : public UDataAsset
{
       GENERATED_BODY()
public:
       UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Platform Configuration")
       float CPUPerfIndex;
       UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Platform Configuration")
       FString Name;
       UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Platform Configuration")
       bool IsIntelCPU;
       UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Platform Configuration")
       int NumPhysicalCores;
       UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Platform Configuration")
       int NumLogicalCores;
       UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Platform Configuration")
       float UsablePhysMemoryGB;
       UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Platform Configuration")
       float ComittedMemoryMB;
       UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Platform Configuration")
       float AvailableMemoryMB;
       UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Platform Configuration")
       float CacheSizeMB;
       UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Platform Configuration")
       float MaxBaseFrequency;
       UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Platform Configuration")
       float CoreFrequency;
       UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Platform Configuration")
       float CorePercMaxFrequency;
       UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Platform Configuration")
       FString FullProcessorName;
       UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Platform Configuration")
       FString ProcessorName;
};

接下来,我们可以使用静态功能设置名为 UPlatformTest 的类,以比较 UPlatformConfig 的属性与插件检测到的功能。

#include "CoreMinimal.h"
#include "PlatformTest.generated.h"
class UPlatformConfig;
/**
 * Static functions for testing capabilities.
 */
UCLASS(BlueprintType)
class CAPABILITYDETECTDEMO_API UCapabilityTest : public UObject
{
       GENERATED_BODY()
public:
       UFUNCTION(BlueprintCallable, Category = "Capabilities")
       static bool CapabilityTest(UPlatformConfig* config);
       UFUNCTION(BlueprintCallable, Category = "Capabilities")
       static UPlatformConfig* GetCapabilityLevel();
};

CapabilityTest() 函数将比较 UPlatformConfig 与功能检测插件检测到的特性。在本示例中,我们将检查物理内核、逻辑内核和 SynthBenchmark CPU 性能索引是否超越传输至函数的 UPlatformConfig 属性。

bool UCapabilityTest::CapabilityTest(UPlatformConfig* config)
{
    // True if system capabilities exceed platform definitions
    return
        UCapabilityDetectBPLib::GetNumPhysicalCores() >= config->NumPhysicalCores
        && UCapabilityDetectBPLib::GetNumLogicalCores() >= config->NumLogicalCores
        && UCapabilityDetectBPLib::ComputeCPUPerfIndex() >= config->CPUPerfIndex;

现在,我们已经找到了一个比较功能的办法,我们可以创建另一个函数来设置与测试平台配置。我们将创建一个名为 GetCapabilityLevel() 的函数,并划分 4 个等级,分别是低、中、高和超高。我们将提供一个与特性等级对应的名称,为每个被测的配置指定物理/逻辑内核和 SynthBenchmark 性能索引。最后,由于我们使用大于等于号来比较 CapabilityTest(),我们将按照从高到低的顺序进行测试,并返回结果。

UPlatformConfig* UCapabilityTest::GetCapabilityLevel()
{
       // Create Platform Definitions
       UPlatformConfig *ULTRA, *HIGH, *MEDIUM, *LOW;
       ULTRA = NewObject<UPlatformConfig>();
       HIGH = NewObject<UPlatformConfig>();
       MEDIUM = NewObject<UPlatformConfig>();
       LOW = NewObject<UPlatformConfig>();
       // Assign Properties to platform definitions.
       // LOW - 2 Physical Cores 4 Hyper-threads
       LOW->Name = TEXT("LOW");
       LOW->NumPhysicalCores = 2;
       LOW->NumLogicalCores = 4;
       LOW->CPUPerfIndex = 0.0;
       // MEDIUM - 4 Physical Cores 8 Hyper-threads
       MEDIUM->Name = TEXT("MEDIUM");
       MEDIUM->NumPhysicalCores = 4;
       MEDIUM->NumLogicalCores = 8;
       MEDIUM->CPUPerfIndex = 50.0;
       // HIGH - 6 Physical Cores 12 Hyper-threads
       HIGH->Name = TEXT("HIGH");
       HIGH->NumPhysicalCores = 6;
       HIGH->NumLogicalCores = 12;
       HIGH->CPUPerfIndex = 100.0;
       // ULTRA - 8 Physical Cores 16 Hyper-threads
       ULTRA->Name = TEXT("ULTRA");
       ULTRA->NumLogicalCores = 8;
       ULTRA->NumPhysicalCores = 16;
       ULTRA->CPUPerfIndex = 125.0;
       // Test platforms against detected capabilities.
       if (CapabilityTest(ULTRA)) {
              return ULTRA;
       }
       if (CapabilityTest(HIGH)) {
              return HIGH;
       }
       if (CapabilityTest(MEDIUM)) {
              return MEDIUM;
       }
       return LOW;
}

C++ 中的检测功能

借助 UCapabilityTest 类,现在我们可以确定 CPU 特性等级。我们可以使用来自 GetCapabilityLevel() 的结果来划分 C++ 或蓝图中的内容。例如,如果我们创建了一个角色,便可以划分 Tick 函数中的特性。

// Called every frame
void AMyActor::Tick(float DeltaTime)
{
       Super::Tick(DeltaTime);
       UPlatformConfig* CapabilityLevel = UCapabilityTest::GetCapabilityLevel();
       if (CapabilityLevel->Name == TEXT("LOW"))
       {
              // Use Simple Approximation for LOW end CPU...
              // e.g. Spawn 100 CPU Particles...
       }
       else if (CapabilityLevel->Name == TEXT("MEDIUM"))
       {
              // Use Advanced Approximation for MID range CPU...
              // e.g. Spawn 200 CPU Particles
       }
       else if (CapabilityLevel->Name == TEXT("HIGH"))
       {
              // Use Simple Simulation for HIGH end CPU...
              // e.g. Spawn 300 CPU Particles
       }
      else if (CapabilityLevel->Name == TEXT("ULTRA"))
       {
              // Use Advanced Approximation for ULTRA CPU...
              // e.g. Spawn 400 CPU Particles
       }
}

蓝图中的检测功能

此外,我们可以将在角色的 Tick 函数中使用的 GetCapabilityLevel() 函数应用于蓝图中,因为我们使用 UFUNCTION(BlueprintCallable) 属性对它进行了修饰。在本示例中,我们使用关卡蓝图并在 BeginPlay 后调用 Get Capability Level 节点。Get Capability Level 节点返回的 UPlatformConfig 值包含一个 Name 属性。在 Switch on String 节点中,它可以用来划分您的等级特性。最后,我们只需将 CPU 特性等级的名称打印到屏幕上(图 1)。


Blueprint Capability Detect
图 1.蓝图功能检测

最后,我们介绍与功能检测插件一起封装的蓝图函数。借助该函数,您可以在蓝图中更细致地发掘平台细节。只需将检测功能节点添加至您的蓝图,然后利用游戏所需的值(图 2)。


Detect Capabilities Blueprint Node
图 2.检测功能蓝图节点

结论

随着现代 CPU 内核数量的增加,我们可以拥有更多的游戏功能。但是,相比配备高端系统的玩家,内核数量较少的玩家可能会处于劣势。为了缩小这种差距,可以使用 C++ 和蓝图划分特性。如前所示,划分特性将实现最大的 CPU 利用率,同时通过一系列平台配置帮助玩家维持固定的帧速率。

Para obtener información más completa sobre las optimizaciones del compilador, consulte nuestro Aviso de optimización.