The world at your fingertips — 天涯明月刀幕後14(性能)

The world at your fingertips — 天涯明月刀幕後14(性能)

來自專欄 遊戲開發隨筆

裁剪

大地形技術帶來了很好的視覺衝擊,也緩解了美術製作效率的問題。

但我們眼睜睜看著遊戲的幀數從60變成30,然後繼續下探,一路沖低到10來幀。在Editor中,因為需要載入整個地圖,所以只有幾幀的效果,基本已經沒有辦法工作了。

在遊戲中還好,因為有streaming系統的存在,我們並不會載入太遠地方的場景。

但隨著大地形效果進一步提升,老於就開始動視距的腦筋了。當時我們的遊戲視距並不算近,也要上百米,但不足以表現開闊的場景。老於各種旁敲側擊,威逼利誘,讓程序員改大了Streaming範圍,同屏可以看見更遠的場景。效果的確很好,大家都很滿意。

但當時美術同學還在艱難的施工中,場景還是空空蕩蕩的,所以引擎還可以跑。隨著施工進行,各類外包的美術資源返回,被整合進了版本,不出意外,內存首先扛不住了。

由儉入奢易,由奢入儉難,既然已經看慣了大視距,就再也沒辦法接受近視距了。我們也不用和老於商量了,他自然還是會要求儘可能保持大視距。我們開始著手優化。

這些年和很多國內開發者接觸過,很多團隊中程序員有著極高的地位,程序員表示不能做,策劃就會屈服。程序員表示要優化美術資源,美術就得苦命的熬夜修改。究其根源,還是在於產品方面同學缺乏一些技術理解力,無法和程序同學有理有據的爭辯,程序同學也缺乏一些擔待,沒有負起該負的責任。

但在天刀項目,老於有充足的技術理解力,且口才好,我也不想去和他爭辯,以免自取其辱。程序團隊也好強且有能力,被美好的願景驅使著,儘力探索技術邊界,給美術和策劃創造更好的開發環境。

內存不夠就分批分優先順序載入,場景中的物件並不都是需要的,根據物件大小、遠近和優先順序,我們盡量少載入物件,但保留最重要的地標性建築。細心調整後程序員放出一大堆參數,基本可以在保持遠視距的同時,合理的偷工減料,既減少渲染,也減少內存,且維持不錯的整體觀感。

當然這類工作說起來容易,做起來就不那麼簡單,依賴美術程序的共同努力,程序先是熬夜找方法,思考如何合理的取捨。即使有了基礎功能支持,美術同學不用刪減素材,但在地圖上做合理的標記,依然是逃不掉的工作量。好在這樣做,美術有一個更崇高的目的,所有的工作不是為了讓遊戲變得更差,而是讓遊戲變得更好,所有的努力,都是為了儘可能保留更遠的視距,雖然還是一樣的加班,但心情想必會有所不同吧。

進一步可以做的事情,是如何更好的管理場景裁剪。大型引擎的裁剪分層,在開始的時候都會使用類似四叉樹之類的技術,做一次高層面的場景管理,第一時刻剔除不需要的物件,不給渲染管線造成更多的麻煩。天刀引擎開發一直遵循精益的原則,場景管理自然不是第一時刻需要做的事情,更何況網遊的streaming機制,天生保證了關卡中的物件都是按需載入,不會有太多的物件。。但美術大規模開工以後,這事情不做不行了,編輯器並不是streaming的,一次載入了所有的內容,即使在高端電腦,幀數也很快就低到不能忍了,影響了美術的工作。

場景管理並不難做。但想想遊戲運行的時候並不需要複雜的場景管理,只為了editor做一下也不太甘心。

這時候,我們發現引擎技術中心的深圳團隊中,Milo老師做了高端的組件,專門處理visibility的culling。這個組件思路和Dice的Battlefield用的裁剪有點像,通過軟體光柵化,模擬一下物件渲染,當然是用很簡化的Bounding Box模擬,然後根據遮擋關係,算出物體的遮擋關係。

比起Dice技術,Milo的技術比較好的地方在於,Dice的技術是在PS3的SPU上算的,效率極高,但PC上並沒有SPU。所有高端的技術都有逆天的優化,Milo用了SSE做了深度的優化,在Core Duo的機器上也只需要1-2ms就可以算完。同時Milo針對我們遊戲的大地形,加入了新的地形裁剪,可以遮擋更多的物件。

肯定有人會說,GPU也有類似的裁剪功能,何必重造輪子。相比GPU的Occlusion Culling,Milo的CPU裁剪庫也有巨大的優勢。因為GPU的culling,結果會慢幾幀,會帶來架構上不必要的複雜性,有一些問題不好處理,對新進入顯示區域的物體,需要各種複雜的處理。而CPU端的Culling,就沒有這些限制,即裁即用,這樣我們的整個渲染和邏輯pipeline,都可以依賴Culling結果,做最精準和暴力的裁剪。

我們趕緊整合這個裁剪庫,瞬間提速2倍,大量物體第一時刻就被裁剪系統丟棄,不影響後續的渲染管線。

然後我們就發現,有了這樣的系統,我們似乎並不需要傳統意義上的scene management系統了,runtime的時候有streaming,控制載入的場景和物件數量有限,渲染的時候用culling,大幅度減少不必要的物件,編輯器中通過culling後,可以做各種激進的優化,而且編輯器中很多物件買的邏輯不用跑,計算量本就可控,這樣看來,四叉樹之類也不需要加上,簡化了整體架構,也降低了運行時對四叉樹的管理成本。

進一步看,我們可以利用這個裁剪做更多的事情,陰影的渲染,生成CSM的階段,水面的反射,也需要裁剪,也可以用上這套裁剪系統。

各種邏輯的Tick,可以考慮可見性,不可見的,過一段時間就不用再tick,這也可以用上裁剪系統。

這套庫提供了一個通用的解決方案,簡單優雅,快速穩定,簡化了架構,提升了性能。

收集

精益開發的自製引擎,沒有太多時間做圖形化的shader編寫系統,material系統都是程序員預先寫好Shader,美術開發調整一些參數為主。開關各種不同材質特性主要靠uber shader。

uber shader會面臨收集shader的問題,因為uber shader本質上就是一個巨大的shader,通過各種宏定義,來生成海量的不同shader。

我們有不同的shader參數組合需要考慮,每一組不同的參數,經過宏處理後,又產生了一個新的shader。這個新生成的Shader,需要被編譯。如果這個shader在在引擎運行時刻編譯,就會Block整個pipeline,造成了卡頓。通常會有500-1000ms的停頓,這自然完全不可接受。

當時並沒有太多精力管這個事情,美術每天催功能,哪有時間細細調整這一塊。

最簡單粗暴的解決方法,就是預編譯所有用到的shader。Tough哥根據shader參數組合,窮舉,在引擎load時候全部編譯shader並載入。這造成了很長的預載入時間,好在只有第一次sync大量代碼才會有,後續就不會有了。

但隨著代碼規模量的擴大,美術的材質參數變多,窮舉顯然不是一個好的方案,編譯時間越來越長,已經無法忍受,佔用內存也越來越多。於是Tough哥打了些補丁,過濾掉一部分不可能出現的材質組合,很大程度減少了需要窮舉的數據集,美術們又愉快的工作起來。

好景不長,臨時的方案撐不了多久的,是時候好好解決一下這個問題了。

不太完美的方法是引擎第一次用到這個shader參數組合的時候,進行後台編譯,用另一個線程去編譯shader。雖然我們初始化D3D設備用了單線程,但編譯shader並不受影響,可以用另一個線程同時處理,只涉及到CPU的計算。當shader編譯完後,進行cache,這樣後續再用到就可以直接讀取cache了。這個方案的缺點主要在於,在第一次遇到這個shader並編譯的時候,因為shader在後台編譯,並沒有準備好,所以相關的物體並不能被渲染,需要等幾幀後才可以被應用到,畫面上會有一些artifacts。

以往做某個Xbox360遊戲的時候就是用這個思路。第一次遇到新shader就編譯,畫面上出現各種醜陋的色塊,一會後台線程編譯完成了,就替換成正確的shader,進行標準的渲染。顯然這個只能用在debug和dev階段,正式版本是沒有辦法放出去的。

為了解決這個問題,我們需要在版本發出之前,儘可能多的玩遊戲,收集各種遊戲中實際會用到的shader組合。每次遇到一個shader,我們就記錄參數到Log文件。主機遊戲有著較大的測試團隊,每天下班前20多個測試人員打了一天的遊戲,把所有人機器上的Log文件都發給我,我寫了個Vim裡面用的腳本,直接合併所有人的Log並去重複,就得到了當時版本的所有shader組合。這個方法並不完美,但考慮到一個單機小體量的遊戲,20多個人,每天可以通過N遍,連著玩幾天,shader參數也就基本收集完整了。

可是天刀項目太龐大了,而且沒有那麼多測試人員,我們沒有辦法依賴暴力測試,來人肉窮舉版本中的所有shader。而且我也不高興再去人肉合併所有的shader參數Log文件,太麻煩了。

路都是人走出來的,憋急了自然有出路。我需要有一個機制,能從開發團隊的日常工作中來收集信息,且需要儘可能自動化,不要影響我自己的工作。另外我需要大量的測試人員去跑遊戲,跑地圖,這個在當時的團隊裡面基本沒法做到。前者的解決方案自然是靠程序自動化來做,後者解決方案是儘可能發動所有的開發人員來做,且不能影響大家的日常工作。

我寫了一個小Server,這個程序只做一件事情,所有開發版本的遊戲,啟動後都會連接到這個伺服器,進行數據通訊,把我需要的數據發送過來 。在這個應用案例裡面,我讓每台開發機器把自己用到的shader組合宏全部發到我的伺服器上,每次用到了新的shader參數,也會通知我的Server。在這個小伺服器裡面,我就可以做各種自動化的合併、去重複工作,這樣版本放出去後,不管是測試人員跑版本,還是美術在編輯器裡面工作,還是程序在開發渲染效果,他們的工作,都會上傳用到的shader組合,對大家來說沒有任何影響。團隊工作兩三天,我的小伺服器上就收集了整個項目所有開發人員用到過的所有shader參數組合。雖然還是有可能不完整,但從外網版本的實際情況來看,已經收集了相當多的量,基本覆蓋了99%的情況。

還需要解決的一個問題,就是無效shader參數的退出機制。每次用到一個shader組合我們就記錄下來,長此以往,肯定shader參數越來越多。偏偏天刀的shader並不穩定,程序員還在不停修改版本,開發特性,總有不少參數組合是會老化廢棄的。於是我又給參數組合一個生命周期,記錄了最後一次使用的時間。如果一個組合很久沒有用到了,就丟棄之。

有了這個系統,我們每次正式發布版本前,就把這個server上的參數組合導出,版本按照這個組合預編譯一遍所有的組合,cache下結果就好了。

顧煜:天涯明月刀幕後(總目錄)?

zhuanlan.zhihu.com圖標
推薦閱讀:

TAG:遊戲開發 | 天涯明月刀遊戲 | 軟體工程 |