<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Vulkan在移動端渲染中的帶寬與同步詳解

冬至子 ? 來源:內核工匠 ? 作者:ningfeili ? 2023-12-01 16:45 ? 次閱讀

1.移動端GPU架構

Immediate Mode Rendering Architecture(IMR)

image.png

早先PC端的GPU大多數采用的是IMR的架構。IMR架構的大體流程如圖所示,值得注意的是對于每個像素,經過數次的color/depth的讀寫,最終繪制到framebuffer上。這當中的每一次讀寫,都是直接與內存交互。所以,頻繁讀寫內存,會消耗大量的帶寬,這個過程會大量發熱。對PC來說,為了更高的畫質和幀率,可以用更好的風扇和散熱從外部解決發熱問題,但是對于空間十分有限的手機來說,肯定不可能去加裝散熱了。

Tile-Based Rendering Architecture(TBR)

所以為了解決帶寬引起的發熱問題,現在的手機GPU普遍使用的是tiled-based架構,Adreno/Mali/PowerVR的GPU在這個基礎上有自家獨有的優化,比如PowerVR的TBDR架構中的HSR,可以完全消滅不透明物體渲染時的overdraw,這里就不展開討論了。

不過它們的核心思想是一致的,都是為GPU開辟了一塊on-chip memory,這塊on-chip memory的特點就是相比于與內存的交互,GPU讀寫它的開銷非常小。

Tile-based rendering將一個完整的framebuffer分為若干個tile,每個tile的內容完全繪制完畢之前,GPU只讀寫on-chip memory,一塊tile完全渲染完畢之后才會將on-chip memory中的內容一次性寫入內存,下圖描述了整體的繪制流程。

image.png

這樣,原本繪制每個像素都需要直接讀寫內存,現在改為全部讀寫開銷極低的on-chip memory,從而節約了帶寬,降低了功耗。

2.為什么用Vulkan?

這也是在移動端渲染開發必然要考慮的問題,要求引擎技術團隊對自己的項需求和Vulkan API,以及相關手機硬件特性都有非常清晰的認知。Vulkan API主要有以下幾點特殊的優勢:

  1. 它的驅動更薄。Vulkan更貼近底層,驅動不會像GLES一樣,進行大量的猜測和判斷,所以用得好可以顯著降低CPU的負載;
  2. Vulkan提供顯式的同步和帶寬控制指令,有效而正確地使用這些指令,可以提升GPU運行效率和降低GPU功耗。
  3. Vulkan 刻錄并提交commandbuffer的架構,更好地支持了多線程渲染。例如Unity PC端實現了用secondary commandbuffer多線程同時渲染同一場景中的物體,UE也利用Vulkan的特性,從渲染線程中抽離出了RHI線程,平均了多核負載,提升了多核效率。
  4. 因為Vulkan標準較新,且受到重視,所以在Vulkan標準定制時,直接包含了很多最新的硬件特性的使用方法。

所以在合理調度的情況下,Vulkan可以享受驅動薄和多線程帶來的CPU收益。而GPU端,在充分了解硬件架構和各個驅動的特點的情況下,手動控制同步和帶寬可以有效提升游戲性能。但是,往往一些項目花了非常多的時間和精力去優化渲染算法,改善渲染流程,或者添加自定義渲染管線,可僅僅是因為對某個硬件特性的不了解,或者用錯了某個API,導致性能下降非常嚴重或者導致優化了的算法反而是負提升,甚至包括早期的Unity、Unreal默認移動端的渲染管線,在這塊都或多或少有些不足。

3.游戲中的帶寬與功耗

先看一個非常簡單的例子。

image.png

上圖是用Unity的rendering commandbuffer實現的一個自定義后處理的渲染流程。當切換rendertarget的時候,如果我們只是使用了默認的接口去設置RT,傳遞到底層,可以看到Vulkan renderpass的load action是load,也就是保留前面的渲染結果。

而當我們使用下面這個接口顯示地設定了rendertarget的loadaction,使Vulkan的load action變為dontcare,可以看到GPU bound的情況下,幀率提升了1幀多。

image.png

這就是因為,當我們load這個rendertarget的時候,在Tile-base架構上,rendertarget從內存中被加載到on-chip memory上,所以產生了額外的開銷。而如果我們顯式地設置了dontcare,就減少了這次加載的時間和帶寬,在GPU bound的情況下,性能的提升直接體現在了幀率上。

Tile-based Rendering在延遲渲染中的應用

image.png

傳統延遲渲染的方式,Gbuffer渲染完畢后,寫入內存,然后lighting階段再sample gbuffers。而因為在lighting階段每個像素需要的是自己像素位置上的gbuffer信息,所以我們完全可以利用subpass,將gbuffer存放在on-chip memory中,后續lighting階段直接從on-chip memory里去讀取。省去了先存內存,再sample的兩次帶寬消耗。

下面是一個延遲渲染的demo,左邊是sample的方式,右邊是則是subpass的方式:

image.png

兩種情況下,lighting的算法,分辨率,燈的數量,fps包括GPU頻率都是固定不變的。唯一的區別就是是否與內存交互,左邊是采樣內存中存下來的Gbuffer;右邊則完全不與內存交互,直接寫入和讀取on-chip memory。

image.png

測試結果可以看到,shader的計算量是一樣的,但是內存頻率和帶寬都是subpass方案有很大提升,這里讀寫帶寬一共減少了4.9Gb/s,內存頻率也有所降低。

image.png

在幀率、GPU使用率和頻率均沒有差別的情況下,純因為帶寬的減少,產生了567mW的功率差,GPU平均溫度也降低了5度。

image.png

這里有一個大致的參考,內存帶寬所產生的功耗幾乎與GPU的功耗持平,,每消耗1GB的內存帶寬,大約會產生120mW的功耗,這也和之前5GB,567mW的測試結果吻合。

不僅僅是延遲渲染,只要渲染流程中想讀取當前像素位置的歷史顏色、深度,都可以用subpass。比如decal,某些粒子效果的半透明渲染、MSAA抗鋸齒等等,合理利用subpass,在帶寬上能得到可觀的收益。

當然,tile-based架構還是有一些缺點的:

比如tiling階段需要將vs全部計算完畢,并且所有的varying要回存內存,所以相比于IMR,除了共有的fetch geometry data的開銷,Tiling階段也有額外的延遲和帶寬消耗。

image.png

所以vertex data的組織也是非常重要的。比如下圖這個自定義的vertex data,aColor將最終傳給fragment shader用作自定義的渲染,

image.png

注意到每個數據都是有效數字只有1位的整數,卻用了32位的格式去存儲。在模型復雜,頂點較多的情況下,這也是一筆不小的開銷。實際上16位,甚至8位的格式就夠了。所以在知曉自己項目每個頂點數據的用途的情況下,我們應當使用盡量小的格式。

Index-Driven Vertex Shading(IDVS)

下圖是ARM上為了減少fetch geometry data的帶寬所做的優化,嚴格意義上它不能算是專門為TBR架構而設計的優化,但它的收益確實很大。

image.png

IDVS的思路就是vs中只先做position相關的計算,等做完剔除之后,才會去fetch其他attribute的數據,做varying相關的計算。但是這個優化要求開發者將position和其他的attribute分buffer存放,這樣GPU才能單獨fetch。

現在市面上絕大多數的手游都是左邊的這種情況,所有vertex data存在了同一個vkbuffer中;右邊則是推薦的做法,一個vkbuffer僅存position data,剩下的varying存在另一個vkbuffer中,在某些vs較重的情況下,兩者的功耗差距超過了30%。所以如果頂點數據復雜,我們還是值得去拆分一下vertex data的。

4.Vulkan中的同步

Vulkan同步中一個非常重要的元素就是pipeline barriers,只要有項目修改了引擎的原生管線,就一定會涉及到pipeline barrier的修改,而實際看下來幾乎所有的項目都有一些使用不恰當的地方。并且,如果項目開發周期比較長,使用的是較早版本Unity、Unreal引擎,那么原生代碼也或多或少有一些使用不當的地方。

以下三點都是Vulkan spec上面提到的pipeline barrier的作用。

第一,它可以控制執行順序,GPU真正執行指令時,并不一定是按照我們提交指令的順序去執行的,所以在指令之間添加一個pipeline barrier,可以保證barrier之前的指令先于barrier之后的指令執行。

只保證指令開始的順序,在并行的情況下,并不能控制指令結束的順序,牽涉到內存修改的情況下就會出現問題。

第二,pipeline barrier還保證了指令間內存的依賴關系,這個后面會詳細解讀。

第三點image的layout轉換其實同樣重要,有興趣的讀者可以查閱相關資料。

Hazards

因為實際情況要復雜得多,這里我們把讀寫模型簡化成GPU core - cache–內存三個部分:

image.png

寫數據時,GPU core完成對cache的修改后要flush cache,將其中的數據拷貝到內存中,這個過程我們稱之為make memory available。

而GPU core讀取數據時,要先invalidate cache,將內存中的數據加載到cache中,這個過程,我們稱之為make memory visible。

了解了這個簡化模型以后,來看內存讀寫的三個hazards:寫后讀、寫后寫、讀后寫。

如果不做同步,比如說寫后讀,很可能讀取指令執行時,前面的寫命令還沒來得及修改內存,從而導致渲染錯誤,這類情況就是需要通過同步指令去避免的。

Pipline Barrier 處理WAR、RAW、WAW的具體流程

三個問題當中最好解決的是讀后寫,對于它來說,只要通過pipeline barrier限制了指令執行順序,就能保證數據讀取的正確性。因為讀取指令先執行,那么數據就已經被加載到了cache中,即使最壞的情況,后面的寫入操作立刻完成,它修改的也僅僅是內存中的內容,無法影響到我們正在讀取的已經被加載到cache中的數據。

接下來我們來看其他兩個稍微復雜一點的同步。

Pipeline stage mask和access mask決定了這個pipeline barrier是對哪段內存生效。那么如果對于同一段內存進行先寫后讀,讀取操作一定要在所有數據都寫入內存之后才能進行。

image.png

所以,在pipeline barrier的保護下,整個同步流程如右上所示:

首先,讀取指令會被block住,等待寫指令執行完畢;srcAccessMask意味著將這段內存make available,保證data寫入內存完畢。dstAccessMask意味著將這段內存make visible,也就是保證剛才被寫入內存的data成功加載到cache中。

最后,執行讀取指令。這樣,整個內存同步就完成了。

寫后寫也是差不多的原理,先阻塞第二次寫操作,等待第一次寫操作完成,且data已經flush到了內存中,再執行后一次的寫操作。

image.png

上面是一個典型的寫后讀的同步例子,這是一個用采樣方式讀取Gbuffer的延遲渲染demo,模擬是游戲中最常見的一種流程,就是先渲染一張RT,然后后續的某個fragment shader采樣這個RT的結果,除了延遲渲染,常見的還有shadowmap,sss,一些實時的風場,腳印的前置流程等等。

右邊是完全正確的寫法,先block住lighting階段的fs,然后把之前Gbuffer階段color output stage的color attachment寫入的這段內存make available,然后再make visible同一段要被shader讀取的內存,再unblock fs,這樣lighting階段的fs就正確讀取到了Gbuffer。

左邊唯一的不同,是把本應等待的fragment階段改成了vertex,這樣就導致GPU會在vertex shading階段提前block住流水線。

這個問題非常隱蔽,首先Vulkan的validation layer不會報錯,因為它只是效率低,而不是一個錯誤。如果粗略地去測試alu,讀寫帶寬,渲染時間,可以看到這些數據在圖上的測試結果幾乎沒有區別。所以這個問題很容易就被忽略過去了。

測試分析工具的使用

各個Soc廠商都提供了自家GPU的分析測試工具,例如 Mali芯片的Mali streamline,PowerVR的芯片的PVRTune,高通的snapdragon profiler。在這個例子中,我們用的手機是Mali的GPU,所以用Mali的測試工具進行分析:

image.png

如上圖,計算量,讀寫帶寬,都沒有差別,唯一有變化的是這個External Bus Read Latency和External Bus Stall。

image.png

具體來看,內存總線讀取數據的阻塞周期有6倍的差距,這就是由于我們的同步指令讓該等待的stage提早到了vertex shading階段。但是由于GPU頻率足夠高,場景也不夠復雜,沒有達到GPU bound,這些延遲并沒有高到足以影響幀率的地步。

現在,讓我們人為地限制GPU頻率,模擬GPU bound的情形,這時內存總線讀寫延遲無法被覆蓋,問題就暴露出來了。

image.png

可以看到,Bus Stall對幀率的影響還是很明顯的,frametime相差了3毫秒,也就是8幀如果沒有進行足夠深入的測試,就很容會忽略掉這個問題,隨著項目向后期推進,渲染壓力逐漸變大,當問題暴露出來的時候,可能已經無法找到最初的原因了。

綜上,同步問題非常重要,我們要完全理解core-cache-memory這個模型,清楚地知道管線遇到的是哪種hazard,正確地使用pipeline barrier,從而充分發揮手動控制同步帶來的性能收益。

其他同步指令

當然Vulkan中還有很多其他的同步指令,例如subpass dependency,和barrier的用法幾乎一樣,但是只能同步renderpass內attachment相關的內存。Semaphore用于隊列之間的同步,fence用于同步GPU和CPU,Event現在手游上用的比較少,目前除了一兩款游戲自己對引擎進行了改動,只有unreal最新的版本在mobile shading renderer的occlusion query部分使用了它。

5.總結

本文首先介紹了移動端渲染架構及其特點,接著闡述了Vulkan API的優勢,再結合實際測試結果分析了Tile-based rendering的優缺點,最后重點介紹了Vulkan的顯式同步控制,結合具體場景和實測數據,給出了優化方案并分析了根因。

希望讀者可以通過本文更加深入地了解Vulkan API以及移動端渲染架構,結合具體的開發場景,合理利用測試分析工具,改善移動端渲染中的功耗、帶寬和同步問題。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • RAW
    RAW
    +關注

    關注

    0

    文章

    20

    瀏覽量

    3757
  • cache技術
    +關注

    關注

    0

    文章

    41

    瀏覽量

    1001
  • Vulkan
    +關注

    關注

    0

    文章

    28

    瀏覽量

    5650
  • GPU芯片
    +關注

    關注

    1

    文章

    303

    瀏覽量

    5697
收藏 人收藏

    評論

    相關推薦

    [8.1.1]--Vulkan介紹_clip001

    Vulkan
    jf_75936199
    發布于 :2023年02月23日 20:39:40

    [8.1.1]--Vulkan介紹_clip002

    Vulkan
    jf_75936199
    發布于 :2023年02月23日 20:40:22

    不能在表面4窗口10上運行vulkan

    還有另一個vulkan-1-999-0-0-0.dll,版本相同,另一個同名的exe也返回上面的結果。我剛下載了適用于Windows *的下載英特爾?系統支持實用程序。我附加了完整的輸出
    發表于 11-12 11:51

    Vulkan同步機制和圖形轉換的風險

    Vulkan同步機制和圖形-計算-圖形轉換的風險(一)
    發表于 01-21 06:17

    Vulkan如何使用barrier

      本篇文章,我們將提到Vulkan 圖形處理過程夾雜計算任務時遇到的各式問題。為更準確地了解我們的話題,可查看文章第一部分?! 〉谝徊糠指攀隽?/div>
    發表于 01-29 06:55

    一文詳解渲染管線

    渲染管線簡單梳理
    發表于 02-03 07:13

    Vulkan的基本類型

    Vulkan基本類型
    發表于 02-19 07:08

    ARM服務器準備如何解決服務渲染的問題

    針對云手機、視頻流云游戲行業,ARM服務器準備如何解決服務渲染的問題?目前的狀況了解,PCIE顯卡對安卓游戲的支持還不夠成熟
    發表于 09-13 14:58

    HarmonyOS/OpenHarmony應用開發-ArkTS語言渲染控制if/else條件渲染

    ; { this.count--; }) } } } if語句的每個分支都包含一個構建函數。此類構建函數必須創建一個或多個子組件。初始渲染時,if語句會執行構建函數,并將生成的子組件添加到其父組件
    發表于 08-21 14:29

    分布式塔臺模擬機中同步渲染算法的研究

    基于分布式的塔臺模擬機中,難點是多機渲染同步問題。文中提出一種使用雙網結構的塔臺模擬機體系結構,并從渲染空間同步、動態數據同步、幀
    發表于 12-14 15:11 ?16次下載

    暢快開黑 一加6T開通王者榮耀Vulkan模式

    更加極致的游戲體驗。 圖:一加6T現已開通Vulkan模式 Vulkan是Khronos 標準組織退出的新一代圖形處理接口(API)標準,其充分考慮到移動平臺多核CPU和GPU協同處理任務的特點,降低功耗的同時還能進一步提高
    的頭像 發表于 11-09 09:30 ?1847次閱讀

    Vulkan編程接口的特征

    Vulkan 簡介 Vulkan是一個用于圖形和計算設備的編程接口。Vulkan設備通常由一個處理器和一定數量的固定功能硬件模塊組成,用于加速圖形和計算操作。通常,設備中的處理器是高度線程
    的頭像 發表于 03-15 17:17 ?1823次閱讀

    FFmpeg將初步支持Vulkan

    近期 Vulkan 勢頭不小,游戲引擎 Godot 計劃年中發布的 4.0 大版本中將支持 Vulkan,Raspberry Pi 也即將迎來 Vulkan 的支持?,F在我們還可以看到 FFmpeg 也將支持
    的頭像 發表于 03-16 09:38 ?2235次閱讀

    Vulkan API 基本類型介紹

    Vulkan 基本類型,Vulkan 開發需要設計的類型非常多,整理其基本類型如下,主要包含設備、隊列、命令緩沖、隊列家族、渲染通,管線等……
    的頭像 發表于 02-12 16:19 ?1554次閱讀

    Vulkan API 基本類型 小結

    Vulkan 基本類型,Vulkan 開發需要設計的類型非常多,整理其基本類型如下,主要包含設備、隊列、命令緩沖、隊列家族、渲染通,管線等……
    發表于 02-23 06:02 ?5次下載
    <b class='flag-5'>Vulkan</b> API 基本類型 小結
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>