手机在线视频精品中文网,色色骚骚操鸡把,精品免费久久久久久久久,中文字幕日韩区二区三区

西山居引擎開發(fā)蘇泰梁:《劍網(wǎng)3:指尖江湖》客戶端性能優(yōu)化方案

2020-12-23 16:19:26

威狐小編:11 月 16 –20 日,中國 Unity 線上技術大會以直播形式召開,為廣大開發(fā)者帶來了一場有關前沿技術和優(yōu)秀案例的線上盛會。在11月20日晚的游戲專場中,來自西山居的資深引擎開發(fā)工程師蘇泰梁,為廣大開發(fā)者詳細講解了《劍網(wǎng)3:指尖江湖》游戲客戶端開發(fā)中所運用到的動態(tài)骨骼技術,以及性能優(yōu)化方案。


以下是演講實錄:

蘇泰梁:大家好,我先做一下自我介紹,我叫蘇泰梁,來自西山居,現(xiàn)在主要負責《劍網(wǎng)3:指尖江湖》優(yōu)化方面的工作,非常榮幸今天有機會來到Unity的線上技術大會分享。

今天主要跟大家分享的是在指尖江湖項目上的優(yōu)化案例,動態(tài)骨骼DynamicBone的優(yōu)化。

我們先來看看什么是動態(tài)骨骼?這里我們可以看兩個視頻。這是我錄制的指尖江湖創(chuàng)建角色的視頻,這是藏劍山莊門派的莊主葉英,大家可以關注一下他的衣服、頭發(fā)和擺動效果和袖子,這個階段我拖拽角色左右晃動一下,可以看到在整個過程中葉英的頭發(fā)、衣服的擺動都是比較真實、自然的,這些地方都用了動態(tài)骨骼的效果,而且基本都是動態(tài)骨骼實時模擬的效果而不是美術K出來的動畫。

當然,動態(tài)骨骼和動作的融合也非常好,比如這段的動作把葉英的衣服吹起來,效果也還可以。接下來我們再看另外一段視頻,對比一下葉英的衣服、頭發(fā)是怎樣的。在這個視頻中,我禁用了所有動態(tài)骨骼的效果,相信大家應該能看出差異,在整個轉動的過程中,他的頭發(fā)、衣服都是硬梆梆的,沒有動態(tài)骨骼樂觀模擬,這個效果就非常差,非常僵硬。

好,什么是動態(tài)骨骼,動態(tài)骨骼其實是一款名叫DynamicBone的插件,它一般用來模擬飄帶、衣袖、裙擺、頭發(fā)等的擺動效果,效果還是比較逼真的,可以大大節(jié)省動作K幀的工作量。在指尖江湖用的比較普遍,比如說玩家、NBC、坐騎、各種趣物、掛件都會用上,是一個用得非常廣的功能。

右圖是指尖的一個角色姬別情,我把它身上用到動態(tài)骨骼的地方都用數(shù)字標識了出來,1、2、3、4,有的長,有的短,都有動態(tài)的效果。

我們先感受一下動態(tài)骨骼的用法,假設我們要給姬別情最長的飄帶加上動態(tài)骨骼效果,這時候我們先找到這根飄帶的根結點的位置,找到它在對象樹上的根結點,然后把它拖到動態(tài)骨骼的組件的節(jié)點上,再配置一下參數(shù)就可以了,非常簡單。

下面有很多參數(shù),比如說阻尼系數(shù),有彈性系數(shù),干性系數(shù),慣性系數(shù)等等很多。阻尼的話可以理解類似一種阻力或者摩擦力,它會減少速度,彈性的話它會把你拉扯到一個目標位置,各種參數(shù)很多,感興趣的同學可以自己研究一下。

好,我們了解了動態(tài)骨骼的用法,再來看看簡化版的模擬過程,動態(tài)骨骼在整個過程中它的簡化的模擬流程是怎樣的。

我們還是以飄帶為例假設它有5個節(jié)點,在上一幀它是處于垂直的狀態(tài),然后在當前幀它稍微往右移了一點,當然它還有一點點的偏移、旋轉,一般都是由于模型的位移或者動作帶來的。除了根結點,其他的節(jié)點都會從上一幀的位置模擬,然后根據(jù)每個節(jié)點的參數(shù),比如前面提到的各種慣性系數(shù)、彈性系數(shù)、各種參數(shù),會對每個節(jié)點進行相關的模擬運算,然后得出一個最佳的位置。

最后,會把這個位置更新到每個骨骼節(jié)點上,同時根據(jù)父子節(jié)點的關系、位置,最新的位置,然后來修正這個旋轉,這樣的話整體上看起來非常自然。

我們再來看看每根骨骼在這個過程中要做的事情。核心的代碼主要集中在組件的Update和LateUpdate中。在Update中,它需要對每根骨骼重置一下它的位置,在LateUpdate中要做大量的模擬運算,并且最終會設置到骨骼,前面也提到。這里面列出了完整的一個模擬運算。除了前面提到的阻尼、彈性之類的,還有風力、重力各種模擬運算,印刷量是比較大的。

我們假設場景里面有30個角色模型,每個模型有10條骨骼鏈,每條骨骼鏈有10根骨骼,那一共有3000根骨骼,在指尖江湖里20個玩家再加上坐騎、NBC之類的,3000根骨骼,這個數(shù)量比較正常。

這么多根骨骼,每根骨骼還要做這么多事情,性能怎么樣?我們可以先看看優(yōu)化前的數(shù)據(jù)。

這是小米Max2在組成動態(tài)骨骼中的CPU消耗,它占了CPU消耗大概10%的占比,這是一個非常大的開銷。這個模塊占了總CPU消耗的十分之一,這確實是非常大的開銷。

動態(tài)骨骼為什么會這么耗?前面提到數(shù)量非常多,一共可能有3000根的骨骼。然后它的運算很復雜,需要做大量的模擬運算。值得一提的是,在整個模擬運算的過程中每根骨骼都要進行世界坐標、世界旋轉、世界矩陣等世界變化的獲取和設置等操作。

說到世界變化相關的操作,在Unity里面要特別注意,因為這是一個非常耗時的操作。比如說獲取和設置世界變化在Unity的頂層并沒有世界坐標的屬性,每次只有局部坐標的屬性。所以,每次獲取或者設置都是根據(jù)父子節(jié)點,一層一層往上變,所以說整個過程非常耗時。

在《指尖江湖》里面,很多骨骼的層數(shù)都是非常深的,比如這張圖。

我看了一下最深大概有20層,這里面確實掛了動態(tài)骨骼的效果,所以它的層級很深,在計算世界坐標、旋轉的過程中開銷是非常大的,層級越深,消耗就會越大。

最后一點就是它的模擬是在Update和LateUpdate中完成的,每幀都需要做,也就是說這是一個固定的常態(tài)性能開銷。

我們了解了它為什么這么耗,現(xiàn)在介紹一下我們做的動態(tài)骨骼第一版的優(yōu)化??梢韵瓤纯聪旅孢@三點,這是我們做優(yōu)化的過程中經(jīng)常提起的一個三原則,第一個是優(yōu)先考慮,能否不做了,吃力不討好,白白浪費工作量的事情最是不應該的,尤其是在性能上。如果不做也能達到效果,那肯定是最好的優(yōu)化,都不做了,那基本上是什么優(yōu)化都沒有,什么開銷都沒有。還是得做的話,再考慮能否少做一些。最后不得不做的時候再考慮能否做得更好,這個可能有點抽象,我們還是具體分析一下。

下面看看《指尖江湖》的這張姬別情的圖,我在1、2、3、4上面再用紅色標出這個骨骼的長度,這個大小下我覺得四根骨骼鏈大家都會看得比較清晰。

好,我們再看這張圖,這張圖很小。

4的話這根飄帶非常長,看得還是比較清晰的,但是1和2是不是已經(jīng)看不大清楚了,尤其是骨骼2我用線連起來,標出來了,其實這個時候它已經(jīng)很短,在這種情況下,即使生硬一點,我估計看不大出來了,尤其是在手機上就更小。所以就有了我們第一版的優(yōu)化思路,根據(jù)骨骼鏈屏幕的投影程度,過短的骨骼鏈就直接關閉動態(tài)骨骼的效果。

投影長度可以使用骨骼鏈靜態(tài)的長度,再加上游戲的FOV,一般動態(tài)變化比較少。所以計算它的屏幕投影長度可以做到幾乎沒有消耗。

一般手機的寬度是70毫米,大概在2毫米以下都看不大清楚。長度還可以根據(jù)機型、機器情況和性能情況分不同的畫質進行定制,當然還可以實時地跟進不同的壓力情況,實時調整。

來能否少做呢?我們還是利用投影長度,可以把適中的骨骼,比如說1或者3的骨骼在某些情況下只保持最基礎剛性的運算,保留最基本的效果。

最后,再考慮能否做得更好。到了這步就只能是死磕算法了,在算法層面進行優(yōu)化,盡可能地減少消耗。這里我們使用的局部坐標,減少世界坐標的操作,因為前面提到了世界坐標的操作是非常耗時的操作,使用一些Catche來減少重復的運算。最后就是減少Component的數(shù)量,一個角色、一個組件就可以支持多條骨骼鏈的配置,因為Component的數(shù)量多存在一些常態(tài)開銷,這就是我們第一版的優(yōu)化。

我們來看看優(yōu)化后的數(shù)據(jù),效果還是比較明顯的,開銷從10%直接降到6.5%,優(yōu)化了大概35%的CPU開銷。這里在總占比中有大概3.5%的開銷,我覺得是比較可觀的。

不過,6.5%的CPU開銷感覺還是挺多,細心的同學我感覺已經(jīng)注意到了,這是第一版優(yōu)化。既然有第一版,那我們就有第二版,接下來再繼續(xù)介紹一下我們第二版的優(yōu)化。

第二版的優(yōu)化,能否再進一步優(yōu)化?最好是不做,或者是少做。這里就需要提到一個概念,就是Unity Job System,這是Unity提供的一套多線程編程框架。我們第二版優(yōu)化的核心思想就是使用多線程,盡可能地減少主線程做的事情,讓別人來干活。

那Job System是Unity提供的一套多線程編程框架,跟一般的多線程有什么不同?為什么Unity需要額外提供一套多線程框架?這個問題在Unity中寫過多線程的人可能都遇到過一個坑,一般的線程沒辦法操作Unity對象,這是Unity的強制限制了。Unity會報錯,并且告訴你說這個只能在主線程訪問,直到Job System出現(xiàn),才使得這成為可能。雖然說現(xiàn)在局限挺大,但是起碼有可能。這就是第一個不同,它使得多線程中操作Unity對象成為可能。

第二個,它有強大的線程安全檢測機制,這個非常重要。比如主線程和Job線程之間一些數(shù)據(jù)的讀寫安全問題,有強大的檢測機制,保證的數(shù)據(jù)不會寫壞。

學過多線程的人我估計都清楚,線程數(shù)據(jù)安全是一個非常頭疼的事情,比如C++里面如果一塊數(shù)據(jù)在多線程里被寫壞了,它可能不會立即出現(xiàn)問題,不知道跑到什么時候突然就宕機了,這個時候你再查就非常困難,因為它已經(jīng)不是第一現(xiàn)場,很早之前就已經(jīng)被寫壞了,這是一個非常頭疼的事情。但是在Job System上可能就不存在這個問題,它的安全檢測機制非常簡單。

第三點,它還有非常高的性能,可以充分地利用多核的CPU。Job System跟Unity引擎頂層的C++共享work線程池,work線程池會通過一個Job隊列來減少上下文的切換和競爭問題,這樣就可以充分地利用多核CPU的資源,從而提高性能。當然,它還有別的優(yōu)勢,我們后面再繼續(xù)介紹。

接下來感受一下Job System簡單的用法。在這里,并行計算兩個數(shù)據(jù)原始物中的一個值相加,然后復制到另外一個數(shù)據(jù)。首先我們要生成一個Job,然后集成Unity I Job相關的接口,只用特定的數(shù)據(jù)結構證明自己的數(shù)據(jù),就可以在Execute函數(shù)中寫需要在Job中運行的邏輯。這個例子也非常簡單,Execute里面就是把A和B的數(shù)據(jù)元素直接相加,然后復制到C的數(shù)據(jù)中。

最后,寫的Job是可以通過Schedule這個函數(shù)把自己的Job推到work線程進行執(zhí)行,使用上非常簡單和直觀。

我們再來看看動態(tài)骨骼Job化的示意。這個是簡化后偽代碼,是按原算法直接轉化后的一個示意。我這里會將耗時的所有操作都Job化。比如Update中的InitTransform操作,它會需要從這幾步坐標。還有就是lateupdate中的各種模擬運算,它對應的就是update particle1函數(shù),update particle2、ApplyParticles ToTransforms,它都會提取到對應的Job中。

但實際上直接轉化成Job是存在不少問題的。我們在直接轉換的基礎上做了很多的加速優(yōu)化,這個后續(xù)會有介紹。

先來看看Job優(yōu)化后的數(shù)據(jù),這是經(jīng)過很多版優(yōu)化后的數(shù)據(jù),相對于上一版的話優(yōu)化了84%的CPU時間,效果是非常非常明顯的。

左邊是優(yōu)化前,右邊是優(yōu)化后的Profiler數(shù)據(jù),我現(xiàn)在已經(jīng)用紅線把work線程的執(zhí)行情況標識出來。我們可以看到左側優(yōu)化前的work線程一直處于idol狀態(tài),什么事都沒做。但是,主線程其實壓力是非常大的,因為所有的東西都放在了主線程來做。

右邊的話是我們Job化后的情況,可以看到所有的Job都在緊密地連接,緊密地執(zhí)行,而且很好地分布到了所有work線程,充分利用了多核的并行加速的效果。

當然,這兩幅圖的時間軸單位是不一樣的,右側是我們不斷地放大后的數(shù)據(jù),主要是為了讓大家看清楚Job的執(zhí)行情況。實際上相對左圖的話,如果按單位來算,大概只有1毫米那么寬,大概是0.15毫秒左右。

這個是我們在原版直接轉化到Job的基礎上經(jīng)過多版優(yōu)化后的數(shù)據(jù)。我們用到了一些比較關鍵的加速手段。接下來給大家介紹一下最關鍵的幾個加速手段。

第一個,讓Job真正并行起來。這個不知道大家看到后有什么感覺,為什么說讓Job真正并行起來呢?難道Job不是并行嗎?我們先看一段我們曾經(jīng)一版的數(shù)據(jù)。我們這一版數(shù)據(jù)說實話優(yōu)化完后發(fā)現(xiàn)優(yōu)化后的數(shù)據(jù)比優(yōu)化前的數(shù)據(jù)還差。大家可以看到上面是主線程,下面是Job線程,主線程一直在等待Job線程執(zhí)行。為什么?這是因為Unity有一個問題,Transform相關的Job只要Transform都在同一個根結點下,它都是沒辦法進行的。這是所有Transform Job繞不開的一個話題。

可能這里有點抽象,我們再看一下我們的使用情況。我們所有的player都放在了一個PlayerSet的分節(jié)點上,在同一個根結點下的對象Transform,它們之間的操作都是不能并行的,所以即使我們每個player對應一個Job,但是實際上跑起來是沒辦法并行的,因為他們都在同一個根結點上。

這會帶來什么問題呢?主要有兩個問題。一個是主線程跟Transform Job是沒辦法并行的,會出現(xiàn)Wait的情況。對應到左下圖,可以看到紅框1里的函數(shù)就是WaitForJob GroupID。這個是因為主線程中有一個跟Transform相關的操作,大家應該可以看到這上面Transform.Get_hasChange。它的核心就是因為他們這個操作的Transform和Job中的Transform是屬于一個根結點,這個時候主線程就需要等待相關的Job線程,相當于沒有在并行,因為主線程一直在等待。

第二個問題就是Transform Job之間也是沒辦法并行的。即使你把Transform相關的幾個Job推到Job線程來執(zhí)行,但是Job線程之間的Transform如果還是屬于一個根結點,它們自己也沒有辦法并行。

對應到左下圖紅框2的Profiler數(shù)據(jù),這一段數(shù)據(jù)大家可以看到其實它只在一個Job線程中執(zhí)行,并沒有像我們前面看到的Profiler數(shù)據(jù)一樣會分散到所有的work線程進行執(zhí)行,還是只在一個Job線程上執(zhí)行,就是它沒有充分利用多核的并行,相當于是一個單線程的Job,這樣效率是非常低的。

為什么會有這個限制呢?實際上父子節(jié)點Transform操作存在關聯(lián)性,前面提到了Transform的世界坐標、操作指令,所以說它可能存在一些安全問題,這樣的話這個線程還是比較能理解。但是非父子關系的節(jié)點,我猜也許為了邏輯統(tǒng)一和簡單。

那怎么樣讓Transform Job并行起來?其實方法也很簡單,我們可以將所有的模型都評估到頂層,比如我們可以把PlayerSet去掉,直接把player全部平鋪到頂層,這樣所有的player之間都可以并行。但是,一個player可能會有十多根骨骼鏈,每根骨骼鏈上面還有十幾個節(jié)點,這些都沒辦法并行。當然,我們還可以再進一步,將每條骨骼鏈平鋪到頂層,比如說player有個尾巴,我直接把這條尾巴拉到頂層。這樣的話,如果它有多條骨骼鏈,所有的骨骼鏈之間每個角色都是可以運行的,當然極限情況是我們還可以把這個骨骼鏈中的每個節(jié)點都平鋪到頂層,這樣的話所有的骨骼節(jié)點在整個過程中都是可以并行。

這很完美,但骨骼平鋪可能會導致關聯(lián)的骨骼動作失效。這是什么問題,什么意思?在某些情況下,動作就會失效,沒動作。為什么會出現(xiàn)關聯(lián)的動作失效的問題?我們先來看看這幅圖。

左邊是角色的對象數(shù),右邊是一個動作文件。這個動作文件它綁定的tail4、tail5這兩根骨骼。Unity在Animator中會根據(jù)動作文件中對應的每根骨骼的名字在左邊的對象數(shù)中進行查找,然后一層一層往上找,找到對應的節(jié)點就可以綁定成功了,如果沒有找到,它就綁定失敗。

當然,實際上是用的名字的哈修,效率肯定會高不少。然后Animator在每次Enable都會做一次綁定,還有一些別的情況下也會觸發(fā)綁定。所以說如果在觸發(fā)綁定的時候把左邊的節(jié)點移走,這個時候動作的綁定可能就會找不到而失敗,動作就沒有效果。

那怎么解決?我們通過源碼發(fā)現(xiàn)可以在引擎頂層加用Catche或者是指定映射關系來解決。什么意思,它還是通過左側對象數(shù)查找的時候可以優(yōu)先使用映射的關系或者是Cache中的數(shù)據(jù),這樣的話即使我們把它的骨骼鏈或者節(jié)點平鋪到頂層,這個時候它重新綁定不會有任何的影響。

當然最后哪一種平鋪策略更合適?具體還是根據(jù)實際情況定的。在《指尖江湖》模型級別的平鋪并行效果已經(jīng)非常好。當然,這跟后續(xù)的各種加速手段的優(yōu)化也是分不開的。

接下來我們繼續(xù)介紹下一個加速手段。

這里再介紹讓Job真正并行起來的另外兩個非常重要的加速手段。我們可以先看看左側兩個Job代碼。上面的Job代碼是從Transform中獲取局部坐標和局部旋轉的代碼。下面那個Job代碼是局部坐標和局部旋轉映射回Transform中,這兩個Transform就是我們跟Transform相關的Job的代碼,已經(jīng)優(yōu)化到了一個極其簡單的程度。

我們?yōu)槭裁匆堰@兩個Transform相關的代碼優(yōu)化到這么簡單?前面提到Transform Job在相同的root點下是沒辦法并行的,很容易跟主線程出現(xiàn)一些wait,所以我們減少Transform相關Job的邏輯,這個時候它跟主線程出現(xiàn)wait的可能性就會降低。當然,即使出現(xiàn)wait,可能wait的時間也不會太長,因為它非常簡單,執(zhí)行起來非??臁?/p>

還有就是非Transform相關的Job是沒有Transform并行的限制,這樣我們就可以把大量的邏輯,幾乎所有的邏輯全部移出來,然后放到非Transform一般的Job中執(zhí)行,一般的Job沒有這個限制,它可以充分地利用work多核的性能,然后充分地并行,這樣它就會大大提速,因為它沒有跟Transform或者是root節(jié)點的限制,這就是我們?yōu)槭裁匆堰@個東西單獨拎出來并且把它簡化到極致。

當然,這里面可能細心的同學還注意到設置的時候最后把它設置到Transform也是用了局部坐標,其實在前面也介紹過,官方在整個代碼中用了大量的世界坐標、世界旋轉、世界矩陣的獲取和設置的操作。其實這些都是非常耗時的操作,我們只獲取了設置局部坐標和旋轉。我們也對比過相應的性能,世界坐標跟局部坐標設置的對比,我們對比了兩者的性能,局部坐標大概有80%以上的性能的提升,提升效果非常明顯。

我們再看看右側的加速手段。這里主要是利用腳本的執(zhí)行順序可以做到一些真正并行加速的效果。比如上面的1就是讓Job在所有腳本中開始執(zhí)行,這樣的話,這個Job線程就可以跟主線程有最大的并行的時間,橫寬的話就是update相關的Job,把它放到了所有腳本的最前面。

另外一個我們可以通過腳本執(zhí)行順序決定Job具體和哪些腳本或者哪些模塊并行。這樣有什么好處?我們可以巧妙地避開這個wait。比如這里下面的紅框我把動態(tài)骨骼放在了UI Pannel的前面,用過NGY的人都知道UI Pannel是NGY中管理所有UI組件的一個模塊,在它的lateupdate中會做大量的核批和填充的操作,這些操作非常耗時。但在這個過程中它肯定沒有用到動態(tài)骨骼的效果,基本上不會在界面上畫動態(tài)骨骼的效果。

它也是一個非常耗時的操作,這樣動態(tài)骨骼跟它并行的話就可以完美地錯開。因為它不用到動態(tài)骨骼的組件,并且它在我們游戲中又是獨立的根結點,不會跟別的玩家或者是NPC組一個同樣的根結點,這樣它就可以完美地跟DynamicBone進行并行,不出現(xiàn)wait。這些加速就可以大大地緩解Transform Job可能出現(xiàn)的wait的情況。

接下來我們再介紹一下加速手段2,它可以大大提速Job的代碼效率。這里的核心是使用Burst Compiler和Mathematics數(shù)學庫的加速。Burst是Unity的一個代碼編譯優(yōu)化工具,它可以針對目標機器進行專門的優(yōu)化。數(shù)學庫的話它是支持SMD的加速,SMD就是一條指令可以同時操作多個數(shù)據(jù)。一般一條指令只操作一個數(shù)據(jù),它因為可以同時操作多個數(shù)據(jù),那在3D運算中,比如向量或者矩陣運算的加速效果非常明顯。

我們看一下兩者數(shù)據(jù)的差別,上圖是沒有開Burst的效果,下面是開了Burst的效果。這里的提升是不是非常的夸張,我第一次也被震驚了,這里起碼50倍以上的性能差異,提升效果非常明顯。

前面我們看數(shù)據(jù)實際上給過一張圖,其實是下面這張圖放大后的效果,它確實只有這么一點點,當然這完全得益于這兩者的加速,如果沒有這兩者的加速,前面即使搞定了并行,估計Job線程還是算不出來,可能主線程還需要一些等待。

使用上非常簡單,Burst和Mathematics都是屬于Unity Pacage中的一個包,就需要在Package Manager中把這兩個導入,在Burst Compiler是需要優(yōu)選相應的包,然后在自己的Job上聲明一個Burst Compiler就可以了,非常簡單。

下面的數(shù)學庫也只需要把它的庫引用進來,然后再使用它的類型就好。比如說使用Float3來替換一般的我們平時用的Vector3,這樣的話這兩個加速就可以用起來,當時的效果極其明顯。

最后給大家再介紹一些加速手段,一個是利用面向數(shù)據(jù)的設計可以減少Cache Missing,核心的話還是利用Unity ECS的思想,盡可能讓Job操作數(shù)據(jù)是連續(xù)的。優(yōu)化前其實每個動態(tài)骨骼都是個組件,數(shù)據(jù)在內存里面都是分散的。當然,每根骨骼更新的時候都需要從不同的內存位置來拿數(shù)據(jù),所以它的Cache Missing非常嚴重。

用Job的話,我們就可以把游戲中所有動態(tài)骨骼數(shù)據(jù)都存在一個連續(xù)的數(shù)據(jù)中,這樣Job就會逐個處理數(shù)字中的數(shù)據(jù),因為數(shù)字的內存是連續(xù)的,所以可以大大地降低Cache Missing,提高數(shù)據(jù)訪問的性能。

第二點就是盡量地減少Job的數(shù)量,Schedule也需要時間開銷。這里可以看一張圖,這是我們曾經(jīng)的一版優(yōu)化,我們拆分了很多的Job,最后發(fā)現(xiàn)Schedule的時間需要很多,有點得不償失。

第三點是盡可能減少數(shù)據(jù)拷貝,可能需要拆分動態(tài)數(shù)據(jù)和靜態(tài)數(shù)據(jù)。前面也看到了Transform相關的Job拆分到Job之后所有的數(shù)據(jù)在主線程做的事情其實是非常少的,經(jīng)驗、核心的邏輯和計算都放到了Job上進行。主線程只剩下更新數(shù)據(jù),并且把更新數(shù)據(jù)到Job,把Job推向work線程,這么簡單的事情。但是可能在動態(tài)骨骼上的一些數(shù)據(jù)更新頻度還是有點高的,一些位置相關的東西,可能更新比較頻繁。

Job用過的人可能都知道,它只支持Structs數(shù)據(jù)類型,而沒辦法使用Calsses,就是它只支持子類型,不能支持引用類型,大家都知道Structs類型的數(shù)組是沒辦法單獨修改元素中某一項屬性,就類似于修改Transform.Position一樣,只能整體布置,沒辦法單獨直接修改,比如直接修改Transform.Position.x,這是沒有效果的,因為position它返回的是一個Structs。

所以我們這里只能用Structs存儲數(shù)據(jù),并且它還需要存在數(shù)據(jù)中,Structs數(shù)據(jù)結構如果全部塞在一起,它的數(shù)據(jù)結構是很大的,因為前面看到它的各種參數(shù)數(shù)據(jù)量是非常大的,這樣需要頻繁復制、拷貝是需要很多時間,而且這個是實打實的在主線程需要做的事情。所以這就需要做到數(shù)據(jù)的動態(tài)分離,拆動態(tài)數(shù)據(jù)和靜態(tài)數(shù)據(jù),現(xiàn)在動態(tài)數(shù)據(jù)更新的數(shù)據(jù)盡可能地少一些,靜態(tài)數(shù)據(jù)基本上都不需要更新,就可以大大地提升性能。

這就是我們最核心的一些加速手段。

最后我們看一下整個Job優(yōu)化后數(shù)據(jù)的總結。假設原版的數(shù)據(jù)是100%,我們在做第一版優(yōu)化之后能減35%,最后通過Job優(yōu)化相對原版就可以減90%的優(yōu)化。這個效果還是非常明顯的,它意味著優(yōu)化前和優(yōu)化后有10倍的性能差異,效果很好。但實際上這一版數(shù)據(jù)是直接跳過了第一版優(yōu)化的數(shù)據(jù)。

實際上我們這版數(shù)據(jù)還沒有考慮到屏幕的裁減等優(yōu)化的數(shù)據(jù),當然結合第一版優(yōu)化的數(shù)據(jù),我們在游戲中已經(jīng)有了,但是由于時間關系,這個數(shù)據(jù)沒有準備上。

最后,我們再來回顧一下今天講的內容。這里講了DynamicBone兩版的優(yōu)化,第一版優(yōu)化主要是常規(guī)的優(yōu)化手段,根據(jù)骨骼鏈的屏幕投影長度來決定它是關閉還是減少運算。第二是在算法上優(yōu)化,局部坐標替代世界坐標或者是加入Cache等等。

第二版優(yōu)化主要是使用多線程,使用Job System,然后可以通過平鋪頂層來解決Transform Job無法并行的問題。另外就是可以簡化和拆分Job和腳本的執(zhí)行順序,解決Job的并行問題。另外,我們可以通過Burst Compiler和Mathematics數(shù)學庫進行加速。最后是介紹了面向數(shù)據(jù)的設計和節(jié)省Job輸入量,還有動靜分離的數(shù)據(jù)。

今天的分享就到這,謝謝大家,大家有任何問題可以在評論區(qū)提問和討論。

404726

0.027523s
张家界市| 渭源县| 洛宁县| 清水县| 双鸭山市| 安化县| 晋宁县| 玛沁县| 广灵县| 平利县| 勐海县| 巴彦县| 满城县| 安乡县| 漾濞| 上饶市| 炉霍县| 乐清市| 松潘县| 台东市| 绥化市| 红安县| 星子县| 章丘市| 呼和浩特市| 合川市| 尉氏县| 商丘市| 门头沟区| 潼关县| 三江| 东乌| 荆州市| 河间市| 镇宁| 金川县| 柳林县| 南康市| 岐山县| 福鼎市| 桓台县|