Unreal Engine* 4 如何助力 Disc Jam* 在英特尔® 处理器显卡上达到 60 fps

大家好! 我是来自 High Horse Entertainment 的 Jay,我们是来自洛杉矶的一个两人团队。 我们最初创立 High Horse 的宗旨是开发具有现代图形、控制方案,以及激烈在线竞争性的街机游戏。 我们的第一个项目 Disc Jam* 是一款街机运动类游戏,游戏成功的关键是时机和反应。 如果您有兴趣一试,可以登录 www.discjamgame.com 免费获取初期测试版的 Steam* 密钥!

性能是该项目的关键因素,因为必须保持每秒 60 帧,才能确保 Disc Jam 的灵活响应和流畅度。 因此,我们学习了大量教程,以确保使用 Unreal Engine* 4 时能够达到这种帧率。 下面我将分享使用英特尔集成图形处理单元的经验,以及如何在不提高最低系统要求的情况下最终实现我们的性能目标。

为何以集成 GPU 为目标?

相比游戏机,由于缺乏硬件标准化,PC 开发更加具有技巧性。 许多玩家都通过 PC 玩游戏,其中一部分玩家拥有独立显卡芯片,而相当大的一部分玩家仍然使用集成 GPU。 我们难以知晓具体的市场规模,但从 Unity Technologies 收集的当今硬件统计数据来看,约 40% 的设备使用英特尔 GPU,这一数字高于其他硬件厂商。 尽管许多 PC 游戏能够通过提高最低系统要求解决问题,但 Disc Jam 必须尽可能降低系统要求,原因有两点:

并发性

Disc Jam 这类多人游戏成功与否取决于并发性。 如果没有玩家玩游戏,玩家则无法找到比赛,因此玩家基础会日益缩小,直到完全消失。 因此,我们必须支持尽可能多的硬件配置。

性能

Disc Jam 的设计游戏帧率为每秒 60 帧 (fps)。 如果玩家的系统无法维持这一帧率,将无法获得预期的游戏体验。 还会影响队友和对手的游戏体验,因为 Disc Jam 首先是一款在线游戏。

Unreal Engine 4 可扩展性和性能

决定采用一种方法时,我们首先考虑的是 Unreal Engine 4 是否能够在我们的目标硬件上实现“即购即用”的性能。 在测试中,我们使用了二进制版 Unreal Engine 4.12.5 和 Shooter Game* 示例。 所有测试均在搭载智能英特尔® 酷睿™ i7-4720 HQ 处理器和英特尔® 高清显卡 4600 GPU 的笔记本电脑上进行。 以 720p 分辨率在 Sanctuary 地图中运行 Shooter Game 时结果如下:


图 1. Epic 质量设置 - ~20fps


图 2. 低质量设置: ~40fps

如果我们游戏的目标帧率是 30 fps,这无疑是个好消息。 遗憾的是,我们真正需要的是达到 60 fps,即使在最低规格中也是如此。 开发优于 Shooter Game 示例的游戏场景绝非易事,我们发现,Unreal Engine 4 (UE4) 桌面渲染器需要较高的基础性能成本,才能实现我们的硬件目标。 幸运的是,如果愿意发挥创意并动手操作,UE4 将能提供一个替代方案。

Unreal Engine 4 的移动预览渲染器

Unreal Engine 不但能够开发高端电脑游戏和主机游戏, 还能开发高端移动游戏! 因此它可提供多种不同的渲染路径,支持市场上的各种移动设备。 我们最感兴趣的是最高端路径,它专门针对面向嵌入式系统 (ES) 的 OpenGL* 3.1 + Android* 扩展包 (AEP) 而设计。我们的测试表明,该渲染路径能够在英特尔集成 GPU 上达到性能与质量的最佳平衡。

其关键在于 UE4 拥有一项名为移动预览 (Mobile Preview) 的功能。 该功能经过精心设计,用户无需进行部署,就可在移动设备上预览游戏内容,从而缩短迭代时间。 它能够有效地支持用户使用移动渲染路径(而非 Unreal 通常使用的完整延迟渲染器)在桌面上渲染游戏。 使用该功能时我们可以看到以下结果:


图 3. OpenGL* ES 3.1 + AEP* 移动预览: ~100fps

在移动预览中运行时,速度比基于最低设置的桌面渲染器快 2.5 倍。 现在我们就能够在集成 GPU 上实现 720p @ 60 fps 的目标! 大家会注意到桌面渲染器的截图与移动渲染器的截图之间存在一些视觉差异。 这是因为移动渲染器存在一些局限性,尤其在灯光和阴影方面。 更多详细信息请参阅 Epic* 有关移动平台的关照移动设备性能指南的文档。

多个光照装置

为解决上述问题,并使 Disc Jam 中球场的光照始终如一,我们选择使用多个光照装置。 我们使用传统渲染器时使用一个光照装置,在移动预览中进行渲染时使用另外一个光照装置。 每款游戏的光照需求各不相同,而 Disc Jam 实际使用相同的光照,唯一的区别是主要投射阴影光源的移动性。 在高端版本中,我们的的主光源是一个静止的聚光灯。 在移动预览中,我们使用静止的聚光灯,以使所有光照都经过预烤,从而帮助释放更多性能。


图 4. Disc Jam* 高端渲染器和光照


图 5. Disc Jam 低端渲染器和光照

在 UE4 中尝试使用多个光照装置时,遇到的第一个问题是如何同时保存烘烤光照和地图中的几何体(而非光照)。 遗憾的是,这意味着你需要将多有几何体复制到另外一张地图中,以便烘烤另一套光源。

就 Disc Jam 而言,我们设置了一个保持不变的游戏关卡,其中的所有角色都不会受到光照的影响。 这些角色可在高端和低端版地图之间共享,并包含生成的点和碰撞体积等内容。 然后,高端地图和低端地图就可包含相同的几何体,唯一的区别只是光照。 加载关卡时,我们流传输至正确版本:


图 6. Disc Jam 不变关卡蓝图

以上使用的“Is in Mobile Preview”节点为自定义 C++ 函数,定义如下:

bool UDiscJamBlueprintFunctionLibrary::IsInMobilePreview()
{
   return GMaxRHIFeatureLevel <= ERHIFeatureLevel::ES3_1;
}

打包和部署

请注意: 以下内容介绍如何在 Windows* 上进行打包和部署。 所示步骤与其他操作系统的步骤大体相同。

打包游戏并尝试通过命令行参数“-FeatureLevelES31”运行后,将会立即确定必要的着色器未包含在软件包中。 Project Settings → Platforms → Windows → Targeted RHIs 下方将显示支持选择使用哪个着色器变体进行打包的复选框,但遗憾的是其中不包含 OpenGL ES 3.1 着色器。 添加该着色器需要进行两项简单的代码修改。

GenericWindowsTargetPlatform.h 中,必须对 GetAllPossibleShaderFormats 函数进行修改,以纳入 OpenGL ES 3.1 着色器:

virtual void GetAllPossibleShaderFormats( TArray<FName>& OutFormats ) const            override 
{ 
    // no shaders needed for dedicated server target 
    if (!IS_DEDICATED_SERVER) 
    { 
        static FName NAME_PCD3D_SM5(TEXT("PCD3D_SM5")); 
        static FName NAME_PCD3D_SM4( TEXT( "PCD3D_SM4" ) ); 
        static FName NAME_PCD3D_ES3_1( TEXT( "PCD3D_ES31" ) ); 
        static FName NAME_GLSL_150(TEXT("GLSL_150")); 
        static FName NAME_GLSL_430(TEXT("GLSL_430")); 

        OutFormats.AddUnique(NAME_PCD3D_SM5); 
        OutFormats.AddUnique(NAME_PCD3D_SM4); 
        OutFormats.AddUnique(NAME_PCD3D_ES3_1); 
OutFormats.AddUnique(NAME_GLSL_150); 
        OutFormats.AddUnique(NAME_GLSL_430); 
    } 
}

然后在 WindowsTargetSettingsDetails.cpp 中修改 GetFriendlyNameFromRHIName 函数,添加一个方便记忆的名称显示在 UI 中:

FText GetFriendlyNameFromRHIName(const FString& InRHIName) 
{ 
    FText FriendlyRHIName = LOCTEXT("UnknownRHI", "UnknownRHI"); 
    if (InRHIName == TEXT("PCD3D_SM5")) 
    { 
        FriendlyRHIName = LOCTEXT("DirectX11", "DirectX 11 (SM5)"); 
    } 
    else if (InRHIName == TEXT("PCD3D_SM4"))
    { 
        FriendlyRHIName = LOCTEXT("DirectX10", "DirectX 10 (SM4)");
    } 
    else if (InRHIName == TEXT("PCD3D_ES31"))
    { 
        FriendlyRHIName = LOCTEXT("DirectXES31", "DirectX Mobile Emulation            (ES3.1)");
    } 
    else if (InRHIName == TEXT("GLSL_150"))
    { 
        FriendlyRHIName = LOCTEXT("OpenGL3", "OpenGL 3 (SM4)");
    } 
    else if (InRHIName == TEXT("GLSL_430"))
    {
        FriendlyRHIName = LOCTEXT("OpenGL4", "OpenGL 4 (SM5, Experimental)");
    }
    else if (InRHIName == TEXT("SF_VKES31"))
    {
        FriendlyRHIName = LOCTEXT("Vulkan ES31", "Vulkan Mobile (ES3.1,         Experimental)");
    }
    else if (InRHIName == TEXT("SF_VULKAN_SM4"))
    {
        FriendlyRHIName = LOCTEXT("VulkanSM4", "Vulkan (SM4)");
    }
    else if (InRHIName == TEXT("SF_VULKAN_SM5"))
    {
        FriendlyRHIName = LOCTEXT("VulkanSM5", "Vulkan (SM5)");
    }

    return FriendlyRHIName; 
}

完成修改并重新编译引擎后,只需勾选 Windows 平台设置下方的方框即可:


图 7. 显示新建的‘DirectX* Mobile Emulation (ES3.1)’

额外说明: 在英特尔 GPU 上自动激活移动预览

Disc Jam 支持玩家通过 Steam 启动选项在游戏启动时选择渲染器。 选择低端渲染器后,只需通过“-FeatureLevelES31’命令行选项启动游戏。


图 8. Disc Jam* Steam* 启动选项

不过,如果使用英特尔 GPU,游戏默认选择移动预览渲染器, 这需要另外进行简单的代码修改。 在 WindowsD3D11Device.cpp 中,函数 FD3D11DynamicRHI::InitD3DDevice() 初始化显卡。 在该函数下方约第 100 行的位置检查您是否使用的是英特尔 GPU,以便对视频内存进行相应的配置。 在该代码段中,我们可对渲染器进行如下设置:

if ( IsRHIDeviceIntel() ) 
{ 
    // It's all system memory.
    FD3D11GlobalStats::GTotalGraphicsMemory =           FD3D11GlobalStats::GDedicatedVideoMemory;
    FD3D11GlobalStats::GTotalGraphicsMemory +=        FD3D11GlobalStats::GDedicatedSystemMemory;
    FD3D11GlobalStats::GTotalGraphicsMemory += ConsideredSharedSystemMemory;
    
    GMaxRHIFeatureLevel = ERHIFeatureLevel::ES3_1;
    GMaxRHIShaderPlatform = SP_PCD3D_ES3_1; 
}

这样就大功告成了!

如果本文对您有所帮助,请在 Twitter 上给我们留言 @HighHorseGames。 如欲关注 Disc Jam 及其开发过程,请查看我们的博客 http://www.discjamgame.com

有关编译器优化的更完整信息,请参阅优化通知