大家好!我是來(lái)自 High Horse Entertainment 的 Jay,我們是洛杉磯的一個(gè)兩人團(tuán)隊(duì)。High Horse 的創(chuàng)立宗旨時(shí)開發(fā)擁有當(dāng)代主流畫面和控制模式,以及激烈在線競(jìng)爭(zhēng)性的街機(jī)游戲。我們的首個(gè)項(xiàng)目 Disc Jam 是一款街機(jī)運(yùn)動(dòng)類游戲,強(qiáng)調(diào)時(shí)機(jī)和反應(yīng)。如您有興趣一試身手,可以在 www.discjamgame.com 免費(fèi)獲取 pre-alpha 版本的 Steam 碼!

性能是這個(gè)項(xiàng)目的重中之重,因?yàn)檫_(dá)到每秒 60 幀才能保證 Disc Jam 快速而流暢的游戲特點(diǎn)。因此,我們?yōu)槭褂锰摶靡?4 達(dá)到該幀率而學(xué)習(xí)了大量教程。以下我將分享針對(duì) Intel 集成 GPU 進(jìn)行開發(fā)的經(jīng)驗(yàn),說(shuō)明如何在不提高最低系統(tǒng)要求的前提下最終達(dá)到性能目標(biāo)。
為什么要以集成 GPU 為目標(biāo)進(jìn)行開發(fā)呢?
因?yàn)槿狈τ布䴓?biāo)準(zhǔn)化,PC 開發(fā)較之于主機(jī)開發(fā)更具有技巧性。在大量的 PC 游戲玩家中,一部分擁有獨(dú)立 GPU,而相當(dāng)大的一部分仍在使用集成 GPU。這個(gè)市場(chǎng)的具體規(guī)模難以得知,但 Unity Technologies 收集的現(xiàn)今硬件數(shù)據(jù)顯示,將近 40% 的游戲電腦使用 Intel 的 GPU,高于其他硬件供應(yīng)商。許多 PC 游戲可通過(guò)提高最低系統(tǒng)要求來(lái)解決這個(gè)問(wèn)題,但 Disc Jam 卻必須盡量降低系統(tǒng)要求,原因有二:
并發(fā)性
Disc Jam 這類多人游戲的興起和衰落皆由并發(fā)性所決定。如果沒(méi)有玩家進(jìn)行游戲,玩家則無(wú)法找到比賽,玩家基礎(chǔ)將縮小,直至完全消失。因此,支持盡量多的硬件配置對(duì)我們來(lái)說(shuō)十分重要。
性能
Disc Jam 的目標(biāo)游戲幀率為 60 幀每秒(fps)。如果玩家的系統(tǒng)無(wú)法維持該幀率,則無(wú)法獲得預(yù)期的游戲體驗(yàn)。這還可能影響隊(duì)友和對(duì)手的游戲體驗(yàn),因?yàn)?Disc Jam 實(shí)際上是一款網(wǎng)游。
虛幻引擎 4 可延展性和性能
決定一種方法時(shí),我們首先在目標(biāo)硬件上“創(chuàng)造性地”審視虛幻引擎 4 的性能。在這個(gè)測(cè)試中,我們使用虛幻引擎 4.12.5 的二進(jìn)制版本和 Shooter Game 范例。所有測(cè)試均在一臺(tái)擁有 Intel Core i7-4720 HQ 處理器和 Intel HD Graphics 4600 GPU 的筆記本電腦上進(jìn)行。Runn

Figure 1. Epic 精度設(shè)置 - ~20fps

Figure 2. 低精度設(shè)置:~40fps
如果我們游戲的目標(biāo)幀率為 30 fps,這將是個(gè)好消息。但我們真心需要達(dá)到 60 fps(甚至在最低配置上也必須如此)。制作出比 Shooter Game 范例更為精簡(jiǎn)的場(chǎng)景已非易事,我們發(fā)現(xiàn)虛幻引擎 4(UE4)桌面渲染器針對(duì)硬件目標(biāo)的基礎(chǔ)性能開銷過(guò)高。幸運(yùn)的是,加上一點(diǎn)創(chuàng)意和技巧,UE4 就能提供一個(gè)替代方案。
虛幻引擎 4 的 MOBILE PREVIEW 渲染器
虛幻引擎不但能開發(fā)高端電腦和主機(jī)游戲。也能開發(fā)高端移動(dòng)游戲!因此它提供了許多不同的渲染路徑,以支持市場(chǎng)上琳瑯滿目的移動(dòng)設(shè)備。我們最感興趣的是最高端的路徑,針對(duì) OpenGL 設(shè)計(jì)的嵌入式系統(tǒng)(ES)3.1 + Android 延展包(AEP)的路徑。我們的測(cè)試結(jié)果表明該路徑在 Intel 的集成 GPU 上能達(dá)到性能和精度的最佳平衡。
關(guān)鍵在于 UE4 擁有一個(gè)名為 Mobile Preview 的功能。此功能的設(shè)計(jì)理念是無(wú)需部署即可在移動(dòng)設(shè)備上預(yù)覽游戲內(nèi)容,從而減少迭代時(shí)間。它允許用戶有效地使用移動(dòng)渲染路徑渲染桌面上的游戲,而不使用 Unreal 通常采用的完整延遲渲染器。使用此功能我們將看到以下結(jié)果:

Figure 3. OpenGL ES 3.1 + AEP Mobile Preview:~100fps
在 Mobile Preview 中運(yùn)行可得到桌面渲染器最低設(shè)置的 2.5 倍速度。這樣我們就能在集成 GPU 上達(dá)到 720p @ 60 fps 的目標(biāo)啦!您會(huì)發(fā)現(xiàn)桌面渲染器的截圖和移動(dòng)渲染器的截圖之間存在一些視覺(jué)差異。這是因?yàn)橐苿?dòng)渲染器存在一些限制,尤其是在燈光和陰影方面。在 Epic 移動(dòng)平臺(tái)的光照和移動(dòng)設(shè)備性能指南 文檔中可查閱到詳細(xì)內(nèi)容。
多個(gè)光照設(shè)備
為解決上述問(wèn)題,使 Disc Jam 中的球場(chǎng)光照始終如一,我們選擇使用多個(gè)光照設(shè)備。使用傳統(tǒng)渲染器渲染時(shí)使用一個(gè)光照設(shè)備,在 Preview 中進(jìn)行渲染時(shí)使用另外一個(gè)。每款游戲的光照需求都不盡相同,而 Disc Jam 實(shí)際使用的是兩套相同的光照,唯一的區(qū)別是主要投射陰影的光源的移動(dòng)性。在高端版本中,主光源為一個(gè)靜止的聚光源。在 Mobile Preview 中我們使用一個(gè)靜止聚光源,因此所有光照均為預(yù)烘焙,有助于榨取更多性能。

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

Figure 5. Disc Jam* 低端渲染器和光照
在 UE4 中嘗試使用多個(gè)光照設(shè)備時(shí)會(huì)遇到的首個(gè)問(wèn)題是烘焙光照將和地圖中的幾何體一同保存,而并非和光照一同保存。不幸的是,這意味著需要將所有幾何體復(fù)制到另一個(gè)地圖中,以便烘焙另一套光照。
我們針對(duì) Disc Jam 設(shè)置了一個(gè)不變關(guān)卡,其中放置的所有 actor 均不受光照影響。這些 actor 在地圖的高端和低端版本之間共享,并包含生成點(diǎn)和碰撞體積域之類的內(nèi)容。之后我們便得到包含相同幾何體的一個(gè)高端地圖和一個(gè)低端地圖,二者只存在光照的區(qū)別。關(guān)卡加載時(shí),將流入正確的版本:

Figure 6. Disc Jam 的不變關(guān)卡藍(lán)圖
以上使用的“Is in Mobile Preview”節(jié)點(diǎn)為一個(gè)自定義 C++ 函數(shù),定義如下:
bool UDiscJamBlueprintFunctionLibrary::IsInMobilePreview() { return GMaxRHIFeatureLevel <= ERHIFeatureLevel::ES3_1; }
打包和部署
請(qǐng)注意:以下部分討論 Windows 上的打包和部署。其他操作系統(tǒng)上的步驟應(yīng)和顯示的步驟較為相似。
打包游戲并嘗試通過(guò)命令行參數(shù)“-FeatureLevelES31”運(yùn)行后,便會(huì)立即明確必要的著色器未包含在包中。在Project Settings → Platforms → Windows → Targeted RHIs 下可找到選擇不同著色器變體進(jìn)行打包的復(fù)選框,但不巧的是 OpenGL ES 3.1 并未包含在內(nèi)。添加此著色器需要進(jìn)行兩段簡(jiǎn)單的代碼修改。
在 GenericWindowsTargetPlatform.h 中,必須對(duì) GetAllPossibleShaderFormats 函數(shù)進(jìn)行修改,將 OpenGL ES 3.1 著色器包含在內(nèi):
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 函數(shù),添加友好名稱顯示 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 平臺(tái)設(shè)置下的框即可:

Figure 7. 新建的“DirectX Mobile Emulation (ES3.1)”將出現(xiàn)
額外說(shuō)明:在 INTEL GPU 上自動(dòng)啟用 MOBILE PREVIEW
通過(guò) Steam 運(yùn)行選項(xiàng)運(yùn)行后,Disc Jam 允許玩家選擇所使用的渲染器。選擇低端渲染器將以“-FeatureLevelES31”命令行選項(xiàng)運(yùn)行游戲。

Figure 8. Disc Jam Steam 運(yùn)行選項(xiàng)
然而,Intel 的 GPU 將使游戲默認(rèn)選擇 Mobile Preview 渲染器。需要進(jìn)行簡(jiǎn)單的代碼修改。在WindowsD3D11Device.cpp 中,函數(shù) FD3D11DynamicRHI::InitD3DDevice() 將初始化顯示適配器。在函數(shù)下方約 100 行的位置,代碼將檢查是否使用的是 Intel GPU,以便對(duì)顯存進(jìn)行正確配置。在該代碼段中,可對(duì)渲染器進(jìn)行如下設(shè)置:
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; }
然后就大功告成啦!