一、簡介
內(nèi)存管理對開發(fā)者來說永遠是一個沉重的話題。
現(xiàn)有的高級語言都在通過各種努力試圖讓開發(fā)者擺脫內(nèi)存管理的復(fù)雜工作,專注于業(yè)務(wù)邏輯的開發(fā)。這樣的做法對開發(fā)者是友好的,較低的語言門檻也能適應(yīng)當前越來越快的敏捷開發(fā)的節(jié)奏。
但是,世上沒有免費的午餐,任何事情都有代價。除了應(yīng)用,沒人更清楚應(yīng)用的行為,有些應(yīng)用需要大量的小內(nèi)存,有些應(yīng)用只需要少量的大內(nèi)存,有些應(yīng)用會頻繁釋放和分頻內(nèi)存資源,有些應(yīng)用的內(nèi)存生命周期則很長。面對復(fù)雜的應(yīng)用場景,我們很難有一個統(tǒng)一的策略可以解決所有問題。不管是通過虛擬機還是驅(qū)動程序,幫助應(yīng)用進行內(nèi)存管理永遠是低效的。
Vulkan將內(nèi)存管理的工作交給了開發(fā)者自己負責,如何分配釋放內(nèi)存,怎樣制定內(nèi)存策略都由開發(fā)者自己決定,因為沒人比開發(fā)者更清楚自己想做什么。
二、內(nèi)存管理
Vulkan將內(nèi)存劃分為兩大類:Host Memory 和 Device Memory。
Host是運行應(yīng)用程序的處理器,在PC機上就是指CPU。
Device是執(zhí)行Vulkan命令的處理器,在PC機上就是指GPU。
所以,Host memory指的是對Host可見的內(nèi)存,Device memory指的是對Device可見的內(nèi)存。
更詳細的,Vulkan系統(tǒng)中的內(nèi)存有四種類型:
Host Local Memory,只對Host可見的內(nèi)存,通常稱之為普通內(nèi)存
Device Local Memory,只對Device可見的內(nèi)存,通常稱之為顯存
Host Local Device Memory,由Host管理的,對Device看見的內(nèi)存
Device Local Host Memory,由Device管理的,對Host可見的內(nèi)存
并不是所有設(shè)備都支持這四種類型。一些嵌入式系統(tǒng)、移動設(shè)備甚至是筆記本電腦的GPU,可能與CPU共享內(nèi)存控制器和內(nèi)存子系統(tǒng)。這種情況它的內(nèi)存只有一種類型,我們通常稱之為unified memory architecture(統(tǒng)一內(nèi)存架構(gòu))。

2.1 Host Memory
Host Memory是CPU可以訪問的常規(guī)內(nèi)存,一般是通過調(diào)用malloc或new分配。
Vulkan API創(chuàng)建的對象通常需要一定數(shù)量的Host Memory,用來儲存對象和數(shù)據(jù)結(jié)構(gòu)。
Vulkan對Host Memory的要求就是內(nèi)存地址是對齊的!
這是因為某些高性能CPU指令在對齊的內(nèi)存地址上效果最佳。通過假定存儲CPU端數(shù)據(jù)結(jié)構(gòu)的分配是對齊的,Vulkan可以無條件使用這些高性能指令,從而提供顯著的性能優(yōu)勢。
如果內(nèi)存沒有對齊,你可能會發(fā)現(xiàn)程序運行一段時間后神秘崩潰!
2.2 Device Memory
任何可以由Device訪問的內(nèi)存,都被稱為Device Memory(設(shè)備內(nèi)存)。Device Memory距離Device更近,比Host Memory更有性能優(yōu)勢。Image object、Buffer object、UBO(uniform buffer objec)都由Device Memory分配。Vulkan中的所有資源都由Device Memory支持。

2.3 Pool
內(nèi)存是一種昂貴的資源,分配操作通常會有間接的系統(tǒng)代價。
Vulkan中通過資源池來平攤成本,一些創(chuàng)建比較頻繁的資源都由資源池統(tǒng)一管理:
Command Buffer Pool
Descriptor Pool
Query Pool
三、Memory and Resources
內(nèi)存的作用是給資源做底層支持,不同的資源對內(nèi)存的要求并不一樣。
3.1 Buffer and Image
Vulkan有兩種基本資源類型:Buffer和Image。

Buffer 是最簡單的資源類型,可以用來儲存線性的結(jié)構(gòu)化的數(shù)據(jù),也可以儲存內(nèi)存中原始字節(jié)。
Image 則相對比較復(fù)雜,具有特殊的布局(layout)和格式(format),可用來做flitering,blending,depth 和 stencil testing等。
3.2 Image Layout
Image的布局(layout)對內(nèi)存有特殊需求,主要有兩種主要的平鋪模式(tiling modes):
linear - 其中的圖像(Image)數(shù)據(jù)線性的排列在內(nèi)存中。
optimal - 其中的圖像(Image)數(shù)據(jù)以高度優(yōu)化的模式進行布局,可以有效利用設(shè)備的內(nèi)存子系統(tǒng)。
線性布局(linear layout)適合連續(xù)的單行的讀寫,但是大多數(shù)圖形操作都涉及在跨行讀寫紋理元素(比如在計算A時,需要參考ABCD四個紋理像素),如果圖像的寬度非常寬,相鄰行的訪問在線性布局中會有非常大的跳轉(zhuǎn)。這可能會導(dǎo)致性能問題。
優(yōu)化布局(optimal layout)的好處是內(nèi)存數(shù)據(jù)根據(jù)不同內(nèi)存子系統(tǒng)進行優(yōu)化,比如將所有的紋理像素都優(yōu)化到一塊連續(xù)的內(nèi)存區(qū)域中,加快內(nèi)存處理速度。

GPU硬件通常傾向于使用優(yōu)化布局以實現(xiàn)更有效的渲染。但是優(yōu)化布局(optimal layout)通常是“不透明的”,這意味著優(yōu)化布局格式的詳細信息不會被其他需要讀取或?qū)懭雸D像數(shù)據(jù)的組件得知。
如果CPU如果想讀取生層的圖像信息,圖像在被讀取之前要進行布局轉(zhuǎn)換,將optimal layout轉(zhuǎn)換為普通布局后,CPU才能正確識別圖像信息。
四、注意事項
4.1 選擇最佳的內(nèi)存使用方式
Vulkan不同的內(nèi)存類型有不同的使用場景:
Device Local Memory適合優(yōu)先級最高的資源,通常是紋理等頻繁被使用的渲染數(shù)據(jù)。當內(nèi)存空間不夠時我們才會考慮是否將這些內(nèi)存數(shù)據(jù)放到CPU端。
Device Local Host Memory 適合CPU 到 GPU的數(shù)據(jù)傳遞,一些頂點數(shù)據(jù)等,可以通過這種方式處理。
Host Local Device Memory 適合GPU 到 CPU的數(shù)據(jù)傳遞,截圖、回寫等操作可以使用這種內(nèi)存內(nèi)省。
4.2 內(nèi)存使用的原則
在Vulkan中,我們在進行內(nèi)存管理時要考慮到以下問題:
內(nèi)存分配通常涉及相當昂貴的系統(tǒng)操作
內(nèi)存重用要比釋放重新分配更快
駐留在連續(xù)內(nèi)存中的對象可以享受更好的緩存利用率。
內(nèi)存對齊的數(shù)據(jù)能獲得更快的處理
4.3 內(nèi)存對齊
在使用Host Memory時,一定要注意內(nèi)存對齊。不允許簡單的直接把malloc hook到Vulkan的內(nèi)存分配函數(shù)上!
參考文檔:
Vulkan Programming Guide
Vulkan Cookbook
Learning Vulkan