對于延遲,不同的人有著不同的衡量方法。例如,從按下按鈕到解碼器予以識別的時間;從系統(tǒng)渲染一陣到屏幕進行顯示的時間等等。Oculus的測量方法則是,從游戲邏輯采樣預(yù)測追蹤到利用這一游戲狀態(tài)渲染的一幀呈現(xiàn)在屏幕中的時間。
對于傳統(tǒng)的游戲幀,一開始都是采樣輸入,執(zhí)行所有邏輯更新,將所有對象渲染到幀;然后,用前置緩存交換后置緩存,從而在屏幕顯示全新的一陣。對于為傳統(tǒng)顯示器設(shè)計的游戲,它們可能會嘗試維持穩(wěn)定的幀速率,如30fps或60fps。但丟失的一幀通常會被忽略,因為游戲中的camera位置和旋轉(zhuǎn)與真實世界的顯示器位置和旋轉(zhuǎn)隔離。對于VR,丟幀會對用戶的舒適度產(chǎn)生嚴重影響,因為只要渲染世界與現(xiàn)實世界不匹配,沉浸幻覺就會被打破。所以,Oculus提供了一個名為異步時間扭曲(Asynchronous TimeWarp)的系統(tǒng),利用最近渲染的一幀,并在屏幕顯示之前對其進行修改,從而令眼睛視圖盡可能接近相應(yīng)的真實世界方向。
這意味著Oculus的渲染管道存在略微的不同。幀更新循環(huán)的第一部分仍然相同:查詢輸入,更新游戲邏輯,然后渲染場景。但接下來,系統(tǒng)不再是交換緩存,而是在渲染時將幀,以及視圖姿態(tài)提交給異步時間扭曲,這樣系統(tǒng)就可以在最后一刻進行修改以匹配更新的視圖姿態(tài)。時間扭曲的巧妙之處在于丟幀發(fā)生時的情形。時間扭曲并不只是將顯示器鎖定在最后渲染的內(nèi)容,它會利用前一幀,但執(zhí)行與更新視圖姿態(tài)相同的邏輯,這樣即便世界的時間狀態(tài)已經(jīng)發(fā)生改變,你的視圖都能匹配真實世界。
1. VSync & Virtual VSync
垂直同步(VSyncl;Vertical Sync)又可以稱為“幀同步(Frame Sync)。這種系統(tǒng)已經(jīng)出現(xiàn)多年時間,而游戲引擎主要是用它來匹配物理顯示器的刷新率。對于VR,由于顯示器的實際繪制交給了異步時間扭曲,所以它同時負責VSync。每一陣都需要固定的時間量,所以如果從這一點開始計算,我們可以定義所謂的Virtual VSync(V-VSync),亦即所有游戲處理都可以圍繞它進行。
請參閱上面的時間表,它暫時忽略了游戲過程。你可以看到,對于每一幀時間扭曲都需要少量的CPU時間,然后在運行VSync時需要一段GPU時間。因此,在運行V-VSync時游戲必須確保幀可供使用,以便時間扭曲能夠處理它們。這當然只是一個簡化的模型,目的是為了介紹時間扭曲所需的處理。
2. Simplest Game Loop
最簡單的游戲循環(huán)都有一個執(zhí)行游戲邏輯的CPU線程:將渲染命令發(fā)送到GPU,然后調(diào)用SubmitFrame,亦即等待下一個V-VSync。類似下圖:
如你所見,游戲邏輯和渲染發(fā)生在一幀長度之內(nèi),而且時間扭曲能夠立即使用渲染幀。從游戲角度來看,這將涉及最低的延遲。如果你的GPU沒有及時完成渲染,時間扭曲將不得不使用最后一幀,并導(dǎo)致渲染幀被丟棄。因為下一幀的CPU工作可以在當前幀的GPU工作仍在運行時運行,所以最終你可能是以全幀速率運行,但會出現(xiàn)多個過時幀。因此,與實際幀速率相比,過時幀的數(shù)量是更重要的監(jiān)控度量。
更糟糕的事情是,GPU渲染時間超過下一個V-VSync。前一幀需要重復(fù)使用兩次,而下一幀的SubmitFrame調(diào)用會被阻止,直至當前幀完成渲染。這為GPU趕上CPU提供了時間,但同時意味當N+1幀最終顯示時,這將出現(xiàn)一整幀的延遲。
事實證明,在一個VSync的正常范圍內(nèi)執(zhí)行全幀渲染是非常難以實現(xiàn)的目標,因為CPU時間、GPU時間加起來需要不到一幀(如72hz時是13.89ms,60Hz時是16.67ms)。實際上,幾乎每款游戲都需要更多的時間。因此,Oculus API支持一種名為“Extra Latency Mode(額外延遲模式)”的功能。額外延遲模式令錯過這一小窗口變成預(yù)期的行為,并始終使用為前一幀提交的幀。所述模式的圖例如下所示:
這樣做的最大優(yōu)勢是,你可以為CPU和GPU利用完整一幀,所以你可以接近于實現(xiàn)100%的利用率。當然,缺點是丟失一幀延遲。Oculus認為,這樣的權(quán)衡折中非常值得,乃至于Unity或Unreal 4都默認開啟額外延遲模式.
如果一切都按時運行,結(jié)果當然是顯而易見,但如果CPU或GPU需要更長的時間才能完整幀的渲染呢?實際上,GPU的情況與這樣一種情況非常類似:當一幀需要兩個以上的V-VSyncs完成渲染時,額外延遲模式?jīng)]有啟用。遲到的一幀將導(dǎo)致下一幀的SubmitFrame調(diào)用被阻止。正如在關(guān)閉額外延遲模式時的情況一樣,當回到預(yù)期的幀周期時,你將呈現(xiàn)至少3個高延遲幀(前一個重復(fù)幀,當前幀和下一幀)。所以,避免GPU運行過長時間對游戲的流暢度而言至關(guān)重要。
CPU的情況沒有那么多問題。在啟用額外延遲模式時,在V-Vsync返回后立即調(diào)用SubmitFrame(假設(shè)前一幀已經(jīng)準備就緒)。例如:
如你所見,果CPU花費的時間繼續(xù)超過幀時間,GPU最終將花費過長的時間,而SubmitFrame會被阻止。但如果CPU時間減少,游戲?qū)⒒謴?fù),應(yīng)用程序?qū)⒂肋h不會丟失幀。
3. 多線程應(yīng)用
盡管單線程應(yīng)用程序最為簡單,但運行Oculus軟件的移動設(shè)備(Gear VR,Oculus Go和Oculus Quest)都擁有具有多個CPU內(nèi)核的芯片組。因此,你需要多線程應(yīng)用來利用這些內(nèi)核。Unity和UE4都提供了多線程渲染模式。
對于這一模式,主線程執(zhí)行游戲邏輯,渲染邏輯則由另一個線程執(zhí)行。所述線程由渲染線程調(diào)用SubmitFrame進行同步,因此要等待V-VSync。當V-VSync觸發(fā)幀開始時,渲染線程向游戲線程發(fā)送信號,以便在操作當前幀時可以開始執(zhí)行下一幀的邏輯。最終效果是,在游戲邏輯和屏幕呈現(xiàn)渲染幀之間發(fā)生一幀的額外延遲。這是一個例子:
類似地,如果渲染線程遲到,則不會發(fā)送信號以通知游戲線程開始下一幀:
4. UE4 and RHIThread
Ureal 4最近推出了一種名為RHIThread的功能。它將圖形API調(diào)用(對于Oculus Mobile,這是OpenGLES或Vulkan)的實際提交與Render Thread完成的其他工作(如剔除和排序等等)分開。對于某些應(yīng)用程序而言,這可以提高性能,因為渲染邏輯從單幀時間拆分為兩個。但是,這需要付出一個額外延遲幀的代價。除非必要,否則大多數(shù)應(yīng)用程序都應(yīng)該避免啟用RHIThread,因為總延遲有可能遠遠超過50ms。
5. 總結(jié)
理解CPU和GPU是如何同步渲染幀是實現(xiàn)最佳性能的關(guān)鍵。如果你的游戲開始丟幀,解決問題的第一步是判斷哪個線程是瓶頸所在?;蛘呷绻阌邢喾吹膯栴},亦即游戲飛速運行,但運動到光子延遲非常高,你可以通過降低線程復(fù)雜性來改善延遲。
原文鏈接:https://yivian.com/news/59536.html
來源:映維網(wǎng)