每每遇到因優(yōu)化翻車的游戲佳作,GameLook都會默默感到惋惜。
光在今年的PC市場,我們就已經見證了多起這樣的事件:《最后生還者重制版》、《臥龍》、《星球大戰(zhàn)絕地:幸存者》……這些玩法上較為上乘的作品,卻因為糟糕的幀率和卡頓等問題在Steam等平臺獲得海量差評。
如果說游戲的底層玩法一般難以改動,優(yōu)化問題大多都是可以通過延長開發(fā)和修繕的時間來解決的“低級問題”。而在發(fā)行商的死線和資金要求等重重因素之下,不少原本優(yōu)秀的游戲卻不得不以不完整的形態(tài)與玩家見面,進而讓口碑受到嚴重影響,功虧一簣。
而在近期,最令人感到可惜的案例無疑是《城市天際線2》了。作為PC平臺最熱門的城市建造類游戲之一,《城市天際線》吸引來了大量粉絲,續(xù)作更是受到萬眾矚目。然而預購了的忠實粉絲們發(fā)現,哪怕自己擁有一塊性能不錯的顯卡,游戲依然會出現嚴重卡頓,甚至在菜單界面的幀率就已經不足20幀了。
隨后,大量的差評如期而至。有Reddit網友通過反編譯發(fā)現,游戲之所以會出現卡頓,很可能是因為游戲渲染了NPC小人的高精度模型,甚至連滿口的牙齒都細致地進行了渲染。在模擬類游戲中往往會同屏出現大量的小人,這種失誤無疑會給顯卡帶來重大的性能負擔。
受到這名Reddit網友的啟發(fā),一名來自芬蘭的程序員Paavo Huhtala決心進一步深挖《城市天際線2》背后的程序設計。與《城市天際線2》的開發(fā)商Colossal Order同為芬蘭人,又是一名模擬游戲的忠實粉絲,Paavo Huhtala在工作之余花了近兩周的時間對這款游戲進行了深入挖掘,足以說明愛有多深。隨著認識的不斷深入,他的反編譯研究逐漸積累成了一篇長達萬字的博文,對渲染的各個通道進行了一一拆解,足夠我們徹底了解翻車的癥結所在。
盡管GameLook并非游戲技術專業(yè)人士,但在洋洋灑灑萬言中凝結的愛與投入也難免讓GameLook受到觸動。在Paavo Huhtala看來,《城市天際線2》的失誤絕非開發(fā)者“擺爛”造成的,事實上,開發(fā)者們對于打造優(yōu)秀畫質有著很高的追求,并試圖引入實驗性的功能進行實現,但最終也因這些功能的不完善而在優(yōu)化上慘烈翻車。
而緊迫的預算和死線,更是讓這款產品不得不“早產”。一款翻車的產品背后沒有贏家——開發(fā)者們的愿景沒有得到實現,發(fā)行商損失了可觀的收入,而玩家們更失去了一款本應十分優(yōu)秀的游戲。
GameLook對Paavo Huhtala的博文進行了全文編譯:
作為今年最受期待的PC游戲之一,《城市:天際線2》于上周發(fā)布,但玩家反響不一。我的印象是,游戲玩法和模擬似乎是朝著正確的方向邁出的一步,至少從紙面上看,這款游戲的功能似乎比原作時更加完善。然而,這款游戲也存在一些重大問題,包括平衡問題、有瑕疵的設計選擇以及導致許多游戲經濟模擬上幾乎毫無意義的漏洞。這款游戲是否能成為原作的優(yōu)秀繼任者仍是一個懸而未決的問題,但有一件事幾乎是普遍認同的,那就是《城市:天際線2》的性能表現完全不達標。
這不是一篇性能評測
實際上,事前早就出現了危險信號。在游戲發(fā)售前一個月,游戲方就發(fā)布了一份公告,告知游戲的推薦系統(tǒng)要求已經提高,且主機版本的發(fā)布被推遲了2024年。不少主播和創(chuàng)作者獲得了游戲的早期體驗權,但他們被明確禁止談論游戲性能表現。這種情況并不罕見,因為游戲往往會在發(fā)行前的最后幾周進行頻繁的性能優(yōu)化和其他修復,但這也不是一個好兆頭。就在發(fā)行前一周,Colossal Order發(fā)布了一份聲明,我將其理解為給游戲的糟糕表現打預防針。然后游戲就發(fā)行了。
像《城市天際線》這樣的模擬游戲很多都難以良好的幀率運行,但《城市:天際線2》的突出之處在于,在大多數情況下,這款游戲都受到了GPU的制約——這對于這類游戲來說很不尋常,因為大多數這類游戲對CPU的要求都很高(比如最初的《城市:天際線》),但對顯卡的要求相對較低。從視覺上看,這款游戲在大多數方面都比2015年的原版有所改進,但并不比光追版的《賽博朋克2077》更難運行。
就我個人而言,我甚至認為《城市:天際線2》看起來很不好看。雖然單個模型更加細致,比例感令人印象深刻,但陰影表現顯然是最后一代的,整個屏幕被渲染工件和過濾不良的紋理所覆蓋。將游戲的圖像與相對接近的競爭對手《紀元1800》(發(fā)行于2019年)完全討不到便宜?!都o元1800》的觀感更加風格化,在我看來,它看起來更加精致和統(tǒng)一,同時即使在2019年被認為是中低端的硬件上也能提供不錯的性能。
我不會浪費時間去仔細地測試游戲,因為許多人已經這么做了,而且他們的評測標準比我高得多。我將總結目前的測試結果:當設置調到最低限度以上(“非常低”的圖像預設,并完全禁用了陰影和霧等高消耗元素)時,你需要一個價值1000到2000歐元的顯卡才能以每秒60幀、1080p的規(guī)格運行游戲。
相比之下,與《城市:天際線2》在同一周發(fā)布的《心靈殺手2》被認為是這一代主機游戲中最好看的游戲,如果你把所有設置都拉滿,包括光線追蹤、原生1440p或4K DLSS,它的配置要求和《城市:天際線2》差不多。我認為這很好地說明了《城市:天際線2》的要求是多么恐怖。
以下是我的一些個人經歷:當我第一次在我的高配游戲PC(配備NVidia RTX 3080顯卡,AMD Ryzen 7 5800X CPU和5120×1440超寬顯示器)上啟動游戲時,我看到主菜單中的幀率低于10FPS。在調整了設置(包括禁用景深、動態(tài)模糊和體積效果)之后,我的FPS達到了將近90。特別奇怪的是,這個菜單只有一個靜態(tài)的背景圖像和幾個按鈕。加載到一個空白的地圖時,幀率大約為30到40 FPS,在玩了大約一個小時后,幀率始終保持在這個水平,偶爾會出現掉幀。讓我們進一步調查。
拉開窗簾
《城市:天際線2》和它的前作一樣是在Unity中制作的,這意味著游戲可以很容易地使用任何.Net反編譯器進行反編譯和檢查。我使用的是Jetbrains,它有一個不錯的類似于Visual Studio的UI,有大量的搜索和分析選項。然而,靜態(tài)分析并不能真正告訴我們任何有關游戲渲染性能的具體信息。為了分析渲染過程,我使用了RenderDoc,這是一個開源圖形調試器,它為我節(jié)省了成本。
讓我們來看看這個游戲的一些基礎技術內容。《城市:天際線2》使用的是Unity 2022.3.7,在撰寫本文時才發(fā)布幾個月。Unity 2022最引人注目的方面是DOTS(多線程式數據導向型技術堆棧)技術的穩(wěn)定,這是Unity已經研究了好幾年的技術。有趣的是,《城市:天際線2》似乎大量運用了DOTS,包括新推出的ECS技術和Burst編譯器。
我已經對Bevy引擎感興趣好幾年了,并且嘗試進行了一些實現,如果不是因為這款游戲中的許多問題,我寧愿寫一篇“ECS是這類游戲的理想架構”的博文?!冻鞘校禾祀H線2》似乎將DOTS發(fā)揮到了極致,因為這款游戲比前作更有效地利用了多個CPU內核。不幸的是,許多與圖像相關的問題都是因游戲使用DOTS間接引起的。稍后我將對此展開討論。
游戲還利用了很多第三方中間件和一些自定義/Fork下來的庫。與DOTS不同的是,Unity的UI Toolkit顯然還沒有準備好投入生產,因為《城市:天際線2》在UI上使用的是Coherent LabsGameface,它是基于HTML、CSS和JavaScript的。簡單看一下JS包就會發(fā)現他們使用的是React,而捆綁使用的是Webpack。雖然這肯定會讓原生開發(fā)的純粹主義者朝天大叫,但我認為至少從理論上,這種模式將使游戲的UI比以前更容易維護和修改。其他值得注意的捆綁庫包括InstaLOD,Odin Serializer和英偉達DLSS 3的DLL文件,盡管該技術目前不受游戲支持。
對于圖形渲染,游戲使用Direct3D 11和Unity的HDRP。Unity的常規(guī)渲染系統(tǒng)只適用于傳統(tǒng)的基于MonoBehaviour的游戲對象,所以使用DOTS和ECS構建的游戲需要用一些其它東西來彌補。Unity有一個名為Entities Graphics的包,但令人驚訝的是,《Cities:Skylines 2》似乎并沒有使用這個包。原因可能是它相對不成熟,支持的渲染特性有限。
根據這個包的技術文檔,皮膚(用于動畫模型,如角色)和遮擋遮蔽(不渲染其他事物背后的東西)功能都被標記為實驗性的,虛擬紋理(使GPU紋理處理更復雜,但更高效)功能則根本不支持。相反,似乎Colossal Order決定利用BatchRendererGroupUnity的和許多相對底層的代碼來自主實現ECS和渲染器之間的粘合。稍后我將更詳細地介紹這一點及其許多含義。
連接問題
將Renderdoc連接到進程并收集渲染事件通常是相當瑣碎的。通常你只需要向Renderdoc提供可執(zhí)行文件的路徑,工作目錄和一些命令行參數,然后Renderdoc啟動二進制文件并將自己注入游戲進程。然而,我的問題是,我的游戲是在Xbox game Pass上運行的,這個版本使用了一些奇怪的沙盒和/或NTFS所有權手法來限制你對游戲文件的操作。由此,RenderDoc不被允許讀取游戲的可執(zhí)行文件,即便作為管理員運行也是如此。在找出Game Pass的問題之前,我也嘗試使用Nsight Graphics英偉達的(類似于NVidia版本的RenderDoc工具),但它也有同樣的問題。最終我用信用卡解決了這個問題:我在Steam上以全價再次購買了游戲,盡管我知道它存在嚴重的問題。對不起。
然而,Steam版本也不是一開始就順利運行。這一次的問題是Paradox啟動器,這是一個用于大多數Paradox發(fā)行的3A游戲的臃腫軟件。啟動器的二進制文件也包含在Game Pass版本中,但至少在發(fā)布時它似乎完全未被使用?;旧蟻碚f,當你從Steam啟動《城市天際線2》時,它會彈出Paradox啟動器,你點擊Resume或Play,然后它就會運行游戲的二進制文件。
我試圖通過直接運行Cities2.exe來連接Renderdoc,但這不起作用——它創(chuàng)建了游戲窗口,但隨后運行了幾秒鐘,打開啟動器,然后退出。在Renderdoc中有一個名為“捕獲子進程”的選項,理論上應該使Renderdoc將自己注入到目標進程啟動的所有進程中——所以它應該將自己附加到由游戲二進制啟動的啟動器上,然后再次注入到游戲二進制中——但我認為有一些額外的間接層不幸地阻止了它工作。
我還嘗試了很多種其他的方法。最終,我終于通過使用RenderDoc的Global Process Hook選項來把它連接到游戲里。事實上,RenderDoc默認隱藏這個選項并建議不要使用,因為這是一種非常具有侵入性的Hook方法,因為它會將DLL注入到系統(tǒng)上啟動的每個進程中,但是,起碼它跑起來了!我們終于可以看到游戲中發(fā)生了什么。
后來我也能成功跑通了英偉達的Nsight Graphics。我沒有嘗試啟動游戲或啟動器,而是從Nsight打開Steam,然后像往常一樣從Steam的UI啟動游戲。但是Nsight種獲得的信息并不比Renderdoc更多,因為似乎許多Nsight的分析和性能重點功能不支持D3D11。
Renderdoc分析
首先我要承認,我不是一個專業(yè)的圖形程序員,甚至不是一個特別精通的業(yè)余愛好者。我偶爾會做圖像編程,也花了很多時間擺弄游戲引擎,但我在這兩方面都不是專家。我從來沒有實現過像延遲渲染或級聯陰影映射這樣的高級功能,盡管我想我知道它們在理論上是如何工作的。所以我接下來要說的一些事情有可能是錯的。如果你認為我錯了,請告訴我。
讓我們從分析以下的實例幀開始:
這是一個相當復雜的幀,但它遠未達到游戲的實際規(guī)模。這是在一個大約有1000名居民的小鎮(zhèn)上,存檔進度大概是進入新游戲后一小時之內。有雨,而且是晚上,但在我的經驗中,這兩種情況對性能都沒有太大影響。游戲版本為1.0.11f1,因此包含了第一個發(fā)布后的熱修復程序。應該注意的是,在撰寫本文時發(fā)布了最新的補?。?.0.12f1),它包含了對我將要描述的問題的一些改進,但還遠遠沒有解決所有問題。
Renderdoc報告說,幀渲染大約需要87.8毫秒,平均約為11.4 FPS。游戲當時的平均幀率是30-40 FPS,所以要么這幀是一個異常值(正如我們從Gamer Nexus視頻中了解到的那樣,諷刺的是這是相當普遍的),要么更有可能是RenderDoc在某種程度上增加了一點性能消耗,影響了測量,因為我捕獲的所有幀都比我在游戲中正常玩時看到的幀數要高一些。我假設即使Renderdoc確實增加了一些消耗,它也不會以一種完全使測量無效的方式添加它,比如使特定的API調用比通常情況下花費10倍的時間。
以下是我從RenderDoc中獲取的一部分數據。
僅從這些數字我們無法推斷出什么。6705個渲染命令和超過50000個API調用聽起來都很多,但如果沒有進一步的情景,它們的成本很難評估。6.7 gb的顯存對于這樣一個相對簡單的場景來說是很多的,特別是考慮到目前仍然有只有8gb VRAM的中低端顯卡。
因為游戲使用HDRP,所以它的文檔可以用來理解游戲在每幀上執(zhí)行的不同渲染和計算傳遞的。我不打算深入研究圖像有多華麗,但我會一步一步地介紹大部分渲染過程,并強調其中一些更有趣的東西。
DOTS實例數據
游戲中幾乎所有的渲染命令都使用了實例化,這在這種規(guī)模的游戲中是必需的。為了進行實例化,游戲有一個大的實例數據緩沖區(qū),其中包含渲染所有對象所需要的一切。每個實例數據的內容和大小因實體類型而異,但似乎建筑等普通游戲對象每個實例大約需要50個點,道路則更多。我還沒有完全弄清楚緩沖區(qū)是如何管理的,因為它是一個非常復雜的系統(tǒng),但基本上每個可見對象的實例數據每幀都會更新到緩沖區(qū),然后將更改上傳到GPU。緩沖區(qū)從大約60兆字節(jié)開始,并在必要時重新分配到更大的大小。
緩沖區(qū)幾乎用于游戲的每個渲染命令,根據Renderdoc,它至少在每個頂點和像素Shader中可用,盡管我認為它主要只用于頂點著色器。了解這個緩沖區(qū)如何影響GPU的緩存將是很有趣的,因為我假設實例不是按照它們呈現的相同順序排列在緩沖區(qū)中,這可能是緩存的一個問題,但我沒有足夠的專業(yè)知識來弄清楚。無論如何,從這個緩沖區(qū)中查找每個頂點的數據都有一定的成本,這可能解釋了一些關于高多邊形網格的問題,我將針對這一點繼續(xù)說明。
模擬
幾個計算著色器用于圖形相關的模擬,如水,雪和粒子,以及骨骼動畫。這些總共花費1.5毫秒,不到幀時間的2%。
一些早期的猜測認為,游戲的性能表現之所以如此糟糕,是因為它可能將大量實際游戲模擬工作轉移到GPU上,節(jié)省了CPU時間,但減少了渲染的處理能力。然而,在查詢了反編譯代碼和GPU調用情況后,我可以得出結論,事實并非如此。