前言本文致力于幫助對(duì)比Vulkan和傳統(tǒng)圖形API之間的各方面差異,幫助大家識(shí)別現(xiàn)代圖形API的優(yōu)缺點(diǎn)所在。 什么是Vulkan?Vulkan是Khronos在2016年的GDC上提出的新一代的圖形API。它提供對(duì)現(xiàn)代 GPU 的更強(qiáng)的控制以及跨平臺(tái)訪問(wèn),這些現(xiàn)代GPU用于從PC和Console到手機(jī)等各種設(shè)備(Window/Linux/Android,在Apple上可以通過(guò)MoltenVk將Vulkan轉(zhuǎn)譯為Metal代碼,也就是說(shuō)Vulkan基本全平臺(tái)通行)。然而Vulkan誕生的最重要的理由是性能,更具體的說(shuō)是優(yōu)化驅(qū)動(dòng)程序和應(yīng)用程序渲染邏輯中的CPU側(cè)負(fù)載。 Vulkan是顯式控制的API也就是說(shuō)幾乎所有的事情開(kāi)發(fā)者都需要親自負(fù)責(zé),但是也提供更強(qiáng)的控制能力。驅(qū)動(dòng)程序只用于接收API調(diào)用傳遞過(guò)來(lái)的指令和數(shù)據(jù),并將它們進(jìn)行轉(zhuǎn)換成硬件可以理解的命令。但是在傳統(tǒng)圖形API(例如OpenGL)里驅(qū)動(dòng)程序會(huì)跟蹤大量對(duì)象的狀態(tài),自動(dòng)管理內(nèi)存和同步以及在程序運(yùn)行時(shí)進(jìn)行狀態(tài)檢查。這對(duì)開(kāi)發(fā)人員非常友好,驅(qū)動(dòng)開(kāi)發(fā)表示負(fù)重前行。但是這種模式顯然會(huì)消耗寶貴的CPU性能。Vulkan解決這個(gè)問(wèn)題的方案是將狀態(tài)跟蹤、同步和內(nèi)存管理交給了開(kāi)發(fā)者,同時(shí)將狀態(tài)正確性檢驗(yàn)交給各種不同的Layer(比如Synchronization Validation和Validation Layer)進(jìn)行完成,而要想使用這些Layer必須手動(dòng)啟用。這些Layer在正常情況下不會(huì)在Vulkan正式版本中應(yīng)用程序里運(yùn)行,僅在Debug版本使用。 由于這些原因Vulkan難以使用并且在一定程度上很不穩(wěn)定。需要做大量的前置工作來(lái)保證Vulkan運(yùn)行正常(比如900行代碼畫一個(gè)三角形),并且API的錯(cuò)誤使用經(jīng)常會(huì)導(dǎo)致渲染錯(cuò)亂甚至Crash,而在傳統(tǒng)圖形API里你通常會(huì)提前收到用于幫助解決問(wèn)題的錯(cuò)誤消息但代價(jià)是性能的損耗。但是Vulkan提供了對(duì)設(shè)備的更多控制能力、清晰的線程模型以及比傳統(tǒng)圖形API高得多的性能。 Vulkan設(shè)計(jì)思想Vulkan的設(shè)計(jì)思想簡(jiǎn)單來(lái)說(shuō)就是以下幾點(diǎn):
Vulkan和傳統(tǒng)圖形API的對(duì)比這有一張表是對(duì)于Vulkan和OpenGL ES的對(duì)比圖,如下所示 下面兩張圖也是很好的對(duì)比了Vulkan和OpenGL的差異,可以看出驅(qū)動(dòng)程序在OpenGL和Vulkan的對(duì)比,Vulkan的驅(qū)動(dòng)程序明顯”薄”了不少。 State Management傳統(tǒng)圖形API使用單一全局狀態(tài),而Vulkan則是使用基于對(duì)象的狀態(tài),可以從下面這兩點(diǎn)提現(xiàn)出來(lái)。 Descriptor并且必須為每個(gè)DrawCall重新設(shè)置渲染狀態(tài)以及資源綁定,所使用的狀態(tài)組合只有在繪制時(shí)才知道,這意味著一些優(yōu)化很難應(yīng)用。并且還隱含一個(gè)問(wèn)題,那就是當(dāng)場(chǎng)景特別復(fù)雜的時(shí)候(幾何多,材質(zhì)復(fù)雜)。這個(gè)設(shè)置渲染狀態(tài)以及資源綁定的過(guò)程所需要的時(shí)間也就不能忽略,但是場(chǎng)景復(fù)雜,大量幾何,材質(zhì)復(fù)雜等正是現(xiàn)代高質(zhì)量渲染所必須面對(duì)的,如下所示。 Vulkan使用描述符(Descriptor)方案來(lái)完成資源綁定,允許應(yīng)用程序提前預(yù)打包已使用資源狀態(tài)的組合??梢越档瓦\(yùn)行時(shí)成本主要是減少各種檢驗(yàn)成本,顯著降低了圖形驅(qū)動(dòng)程序的CPU開(kāi)銷。代價(jià)是要求預(yù)先確定它需要的狀態(tài)以便構(gòu)建狀態(tài)對(duì)象并從減少的開(kāi)銷中受益。傳統(tǒng)圖形API將不得不在改變著色器時(shí)檢查所有的資源綁定,而對(duì)哪些是被覆蓋的,哪些是需要保留的,因?yàn)樗鼪](méi)有足夠信息可以用來(lái)判斷并做出優(yōu)化。 PipelineVulkan在這方面設(shè)計(jì)理念還可以在Pipeline創(chuàng)建當(dāng)中體現(xiàn)。Pipeline包含了傳統(tǒng)圖形API中大部分的狀態(tài)和屬性。只不過(guò)Pipeline是需要事先創(chuàng)建好的,這樣所有的狀態(tài)組合的驗(yàn)證和編譯都可以在創(chuàng)建Pipeline的時(shí)候完成,編譯好后的Pipeline對(duì)象結(jié)合了所有相關(guān)狀態(tài),允許更可預(yù)測(cè)地應(yīng)用基于Shader的優(yōu)化,從而降低運(yùn)行時(shí)成本。并且驅(qū)動(dòng)程序只需要少量的切換開(kāi)銷即可將預(yù)先創(chuàng)建的Pipeline綁定到GPU中。運(yùn)行時(shí)不會(huì)再因?yàn)檫@些操作有任何性能上的浪費(fèi)。 這種提前構(gòu)建的方式有著性能的優(yōu)勢(shì)但是不夠靈活,可能只是需要修改一個(gè)屬性卻需要?jiǎng)?chuàng)建一個(gè)新的Pipeline。但是Vulkan也為Pipeline 提供了一些Dynamic State,這些狀態(tài)仍然可以被動(dòng)態(tài)改變,無(wú)需付出重新創(chuàng)建Pipeline的成本。這也為Pipeline提供了一定的靈活性。 使用方式不同狀態(tài)管理的不同會(huì)造成API使用方式的不同,在之前OpenGL的使用中都是全局狀態(tài),每一次的API調(diào)用你都需要知道將要設(shè)置的OpenGL狀態(tài),所有的狀態(tài)都由OpenGL的VM進(jìn)行管理,因此在使用OpenGL時(shí)需要時(shí)刻將VM的狀態(tài)熟記于心。不然很容易出錯(cuò)但是可以不考慮底層硬件的設(shè)計(jì)或復(fù)雜性。但是在Vulkan中是通過(guò)Pipeline來(lái)完成,但是Pipeline的創(chuàng)建需要相當(dāng)繁瑣的參數(shù)設(shè)置,而且你都得理解這些參數(shù)的意義。從使用方式來(lái)看很難說(shuō)哪種更好。 API execution model對(duì)于之前傳統(tǒng)圖形API(比如OpenGL),OpenGL是一種同步(Synchronous)模型去執(zhí)行的,這意味著 API 調(diào)用必須表現(xiàn)得好像所有先前的API調(diào)用都已被處理。實(shí)際上沒(méi)有任何GPU是以同步模型的方式去執(zhí)行Command而是全部都是異步執(zhí)行的。同步模型是由驅(qū)動(dòng)程序維護(hù)的精心制作的假象,厚重的驅(qū)動(dòng)程序在為你負(fù)重前行。為了保持這種錯(cuò)覺(jué),驅(qū)動(dòng)程序必須跟蹤Queue中的每個(gè)渲染Command讀取或?qū)懭肓四男┵Y源確保所有Command以合法順序運(yùn)行以避免渲染結(jié)果錯(cuò)亂,并確保需要數(shù)據(jù)資源的API調(diào)用被阻塞并等待資源是安全可用才可繼續(xù)運(yùn)行。 那么這種同步模型帶來(lái)了什么問(wèn)題呢?第一是某些耗時(shí)的操作觸發(fā)時(shí)機(jī)并不穩(wěn)定并且全部由驅(qū)動(dòng)來(lái)確定,比如當(dāng)切換了一個(gè)Pipeline時(shí)觸發(fā)一個(gè)耗時(shí)的Shader編譯操作或者加載資源等等,這些操作什么時(shí)候會(huì)去做,是不是已經(jīng)做完了?這些對(duì)于開(kāi)發(fā)者來(lái)說(shuō)都一無(wú)所知并且全部由驅(qū)動(dòng)決定。簡(jiǎn)單來(lái)說(shuō)就是開(kāi)發(fā)者無(wú)法精細(xì)的控制當(dāng)前渲染過(guò)程中的所有狀態(tài),雖然每個(gè)圖形API提供了獲取當(dāng)前狀態(tài)接口,但幾乎所有的官方文檔中都建議不要頻繁調(diào)用這些接口以避免由于獲取狀態(tài)導(dǎo)致額外的調(diào)用開(kāi)銷??赡茉斐傻慕Y(jié)果就是CPU卡頓,但是卻不知道卡頓是發(fā)生在什么時(shí)候,可能會(huì)發(fā)現(xiàn)切換一個(gè)Pipeline或者是加載一個(gè)資源卡頓就出現(xiàn)了。并且由于每個(gè)廠商的GPU處理這些工作的方式都不一樣,在不同的GPU上可能會(huì)有不同的表現(xiàn),想專門優(yōu)化的話都會(huì)被這些攔路虎(黑盒)攔住而無(wú)從下手。 Vulkan使用異步(ASynchronous)渲染模型,首先是符合GPU的工作方式的。Vulkan通過(guò)Command Buffer來(lái)Record Commmand之后就塞入Queue,使用顯式調(diào)度依賴關(guān)系來(lái)控制渲染任務(wù)執(zhí)行順序以及CPU和GPU之間的同步以及依賴關(guān)系管理等,通過(guò)精心調(diào)控的同步可以提高渲染Command執(zhí)行的整體并行度。 減少Pipeline的氣泡并提高整體性能。并且結(jié)合了之前的關(guān)于提前創(chuàng)建Pipeline對(duì)象以并綁定。在渲染時(shí)可以切換不同的Pipeline而只需要很小的開(kāi)銷而無(wú)需像傳統(tǒng) API 那樣校驗(yàn)Pipeline狀態(tài)有效性以及動(dòng)態(tài)合并一些狀態(tài),從而降低了Draw Call開(kāi)銷,并且可以大幅增加每幀可以調(diào)用的DrawCall上限。 這些更改的影響是顯著降低了圖形驅(qū)動(dòng)程序的CPU開(kāi)銷,但代價(jià)是要求程序員自己管理Command之間的執(zhí)行依賴和內(nèi)存依賴來(lái)保證渲染結(jié)果的正確,加重了開(kāi)發(fā)者的心智負(fù)擔(dān)。 這一張圖更加能夠體現(xiàn)OpenGL和AZDO(Approaching Zero Driver Overhead) OpenGL以及Vulkan的區(qū)別和特點(diǎn)。OpenGL就像一個(gè)預(yù)先已經(jīng)組裝好的玩具車,開(kāi)箱即用,沒(méi)有太多定制的空間。到了AZDO OpenGL就像一個(gè)樂(lè)高一樣。你可以自由的去建造并且配有大量有用的預(yù)制件。Vulkan只有最基礎(chǔ)的配件,你必須自己先雕刻成你想要的樣子,自由度相當(dāng)?shù)母摺?/p> 這很符合上面說(shuō)的說(shuō)的內(nèi)容,在OpenGL中驅(qū)動(dòng)程序幫助我們做了很多的東西,但同時(shí)也帶來(lái)了很多限制。雖然對(duì)于十分的易用,但是沒(méi)有定制的空間。而Vulkan是將之前OpenGL在驅(qū)動(dòng)程序中做的那些操作,更多的開(kāi)放給開(kāi)發(fā)者自己來(lái)管理。包括內(nèi)存管理以及CPU和GPU之間的同步管理等等。這給了開(kāi)發(fā)者極大的定制空間,并且解放驅(qū)動(dòng)程序CPU側(cè)的開(kāi)銷性能更好。 API threading model傳統(tǒng)的圖形API對(duì)多線程并不友好,并且傳統(tǒng)圖形API都包含一個(gè)Context概念。Context包括當(dāng)前Pipeline中的所有狀態(tài)包括Shader以及Render Target等。在OpenGL中Context和單一線程是綁定的所有需要作用于Context的操作例如改變渲染狀態(tài)/綁定Shader/調(diào)用Draw Call等都只能在單一線程上進(jìn)行。如下所示可以看到只有一個(gè)線程在完成CPU側(cè)的渲染前的處理(渲染狀態(tài)設(shè)置,狀態(tài)綁定,DrawCall提交等等),其他的線程都在看戲(類似Window11的大小核調(diào)度,一核有難,八方圍觀),這造成了渲染中的CPU側(cè)的瓶頸以及GPU占用率低下的問(wèn)題。 但是現(xiàn)在多核系統(tǒng)可謂是相當(dāng)?shù)钠毡?。但是傳統(tǒng)圖形API卻不能利用這多核資源。當(dāng)發(fā)現(xiàn)是主要是CPU瓶頸的時(shí)候,通過(guò)CPU多線程應(yīng)該可以很好的解決性能瓶頸問(wèn)題。所以Vulkan的設(shè)計(jì)理念首先就是對(duì)于多線程友好,充分發(fā)揮多線程的優(yōu)勢(shì)以減少CPU側(cè)的壓力并且可以充分壓榨GPU的性能(狠狠壓榨GPU,塞滿他?。?,于是設(shè)計(jì)出了Command Buffer概念并且拋棄了Context以及渲染線程等概念。在Vulkan中必須開(kāi)發(fā)者自己管理Command Buffer并且每個(gè)線程都可以通過(guò)Command Buffer來(lái)完成Record Command操作(也就是多線程錄制),并以適當(dāng)?shù)牧6忍峤粓?zhí)行給Queue中讓GPU開(kāi)始執(zhí)行以避免浪費(fèi)GPU的性能,如下圖所示。但是單線程的Vulkan渲染器的速度仍然可以明顯快于傳統(tǒng)圖形API(但主要需要精細(xì)的內(nèi)存管理以及同步做到的),但通過(guò)利用系統(tǒng)中的許多核心進(jìn)行Record Coomand,可以獲得峰值效率和最小延遲。 尤其是在復(fù)雜場(chǎng)景的情況下,這也是Vulkan相比傳統(tǒng)圖形API最能體現(xiàn)性能提高的情況。那就是并行的在不同線程上生成場(chǎng)景不同部分的Command Buffer,不用任何線程間的Synchronization。最后不同的線程可以將Command Buffer的傳給主線程然后由主線程將它們提交給Queue,也可以直接寫入子線程中的直接提交給Queue。這樣的模式達(dá)到了計(jì)算資源利用的最大化,多個(gè)CPU核都參與了場(chǎng)景的渲染,并且有大量的渲染任務(wù)同時(shí)遞交給GPU最大化了GPU的吞吐量。 API error checking傳統(tǒng)圖形API為了體現(xiàn)易用性的設(shè)計(jì)哲學(xué),在驅(qū)動(dòng)程序中往往都帶有非常復(fù)雜的邏輯,但是進(jìn)行運(yùn)行時(shí)的校驗(yàn)也是驅(qū)動(dòng)來(lái)保證的以保證渲染正確。同時(shí)為了便于調(diào)試,圖形 API 的獲取錯(cuò)誤接口(glGetError)也需要正確的返回錯(cuò)誤,但也增加了 CPU 開(kāi)銷。許多錯(cuò)誤是由編程錯(cuò)誤導(dǎo)致的,但是這些錯(cuò)誤只會(huì)在開(kāi)發(fā)過(guò)程中發(fā)生并且在運(yùn)行時(shí)無(wú)法有效處理,但運(yùn)行時(shí)檢查仍然必須進(jìn)行,這增加了所有應(yīng)用程序的驅(qū)動(dòng)程序開(kāi)銷。 Vulkan 是圍繞最小化驅(qū)動(dòng)開(kāi)銷的理念設(shè)計(jì)的,這一目標(biāo)的表現(xiàn)之一就是API中默認(rèn)的錯(cuò)誤檢查非常有限。即使是將參數(shù)設(shè)置為不正確的值或?qū)⒖罩羔槀鬟f給所需的參數(shù)這樣簡(jiǎn)單的錯(cuò)誤通常也不會(huì)被明確處理而會(huì)導(dǎo)致崩潰或未定義行為。因?yàn)閂ulkan要求你對(duì)你所做的一切都要非常明確的控制。然而這并不意味著這些錯(cuò)誤檢查不能被添加到API中。Vulkan 提供了一個(gè)框架允許在應(yīng)用程序和本機(jī) Vulkan 驅(qū)動(dòng)程序之間插入Validation Layers。Validation Layers可以實(shí)現(xiàn)錯(cuò)誤檢查和其他調(diào)試功能,并且具有可以在不需要時(shí)將其刪除的主要優(yōu)點(diǎn)。 這些更改的影響是減少驅(qū)動(dòng)程序 CPU 負(fù)載但代價(jià)是使許多錯(cuò)誤無(wú)法檢測(cè)到除非使用Validation layers。 RenderPass abstraction傳統(tǒng)圖形API大都沒(méi)有RenderPass的概念,因此驅(qū)動(dòng)程序必須在運(yùn)行中推斷出哪些渲染命令構(gòu)成一個(gè)單獨(dú)的RenderPass。該任務(wù)需要一些處理時(shí)間并且依賴于可能不準(zhǔn)確的啟發(fā)式方法。 Vulkan是圍繞RendPass概念構(gòu)建的,旨在讓應(yīng)用程序?qū)⒁粠母邔咏Y(jié)構(gòu)傳遞給驅(qū)動(dòng)程序更加明確渲染執(zhí)行的步驟。并且Tile Base架構(gòu)的GPU驅(qū)動(dòng)程序可以使用這些信息來(lái)確定何時(shí)將數(shù)據(jù)進(jìn)入或者離開(kāi)On-Chip內(nèi)存并且判斷是否需要將數(shù)據(jù)放置到內(nèi)存或者是丟棄Tile內(nèi)的全部?jī)?nèi)容,甚至做一些其他事情比如用于binding大小的內(nèi)存分配和其他內(nèi)部操作。 Memory allocation傳統(tǒng)圖形API(比如OpenGL)使用Client-Server內(nèi)存模型。該模型明確劃分了可在客戶端(CPU)和服務(wù)器(GPU)上可以訪問(wèn)的資源,并提供了在兩者之間移動(dòng)數(shù)據(jù)的函數(shù)。這有兩個(gè)主要的副作用:首先開(kāi)發(fā)者不能直接分配或管理GPU側(cè)資源的內(nèi)存。驅(qū)動(dòng)程序?qū)⑹褂脙?nèi)部的內(nèi)存分配器單獨(dú)管理所有的GPU資源,并且驅(qū)動(dòng)程序不知道任何可以利用來(lái)降低內(nèi)存成本的上層信息。其次在CPU和GPU之間同步是有代價(jià)的,特別是在API的同步渲染模型和GPU異步處理現(xiàn)實(shí)之間存在沖突的情況下。 Vulkan是為現(xiàn)代GPU設(shè)計(jì)的,并假定CPU和GPU可見(jiàn)的內(nèi)存設(shè)備之間存在某種程度的硬件支持的內(nèi)存一致性。這使得API能夠讓應(yīng)用程序更直接地控制內(nèi)存資源比如分配/更新數(shù)據(jù)。對(duì)內(nèi)存一致性的支持允許Buffer在應(yīng)用地址空間中保持持久的映射,避免了傳統(tǒng)圖形API為注入手動(dòng)一致性操作而需要的連續(xù)映射-解映射周期。這些更改的影響是減少驅(qū)動(dòng)程序的CPU開(kāi)銷并讓開(kāi)發(fā)者更好地控制內(nèi)存管理。并且可以進(jìn)一步減少CPU負(fù)載,例如通過(guò)將具有相同生命周期的對(duì)象分組到一個(gè)分配中并跟蹤它,而不是單獨(dú)跟蹤它們。 根據(jù)Vulkan的設(shè)計(jì)思想。當(dāng)然對(duì)于內(nèi)存管理這塊Vulkan會(huì)把這部分的控制權(quán)交由開(kāi)發(fā)者,讓開(kāi)發(fā)者自己來(lái)管理。更加依賴于程序員自身的資源理解,來(lái)做到更好的優(yōu)化。所有的內(nèi)存的分配和釋放控制權(quán)全部在開(kāi)發(fā)者。這些變化的影響是降低了驅(qū)動(dòng)程序的CPU負(fù)載,使開(kāi)發(fā)者對(duì)內(nèi)存管理有更多的控制。應(yīng)可以進(jìn)一步減少CPU負(fù)載,例如將具有相同生命周期的對(duì)象分組到一個(gè)分配中并進(jìn)行跟蹤,而不是單獨(dú)跟蹤它們。 Vulkan甚至允許將一個(gè)Buffer內(nèi)的不同區(qū)域劃分給格式或者用途不同的子Buffer,例如Index/Vertex Buffer可以共享同一個(gè)Buffer,只要在綁定的時(shí)候指定不同的offset即可。這也是最優(yōu)的做法,它既減少了內(nèi)存分配的次數(shù)也減少了Buffer綁定次數(shù)。這種做法也符合了Vulkan關(guān)于資源復(fù)用的設(shè)計(jì)思想。如下圖所示: 至于如何組合這些資源和對(duì)應(yīng)的布局,那就是需要開(kāi)發(fā)者自己去處理并且找到最好的數(shù)據(jù)/資源布局以及相應(yīng)的更新/綁定頻率。不需要像傳統(tǒng)圖形API那種在驅(qū)動(dòng)中的啟發(fā)式優(yōu)化。開(kāi)發(fā)者自身也最清楚這些數(shù)據(jù)/資源的更新頻率。永遠(yuǎn)都能比驅(qū)動(dòng)分析的結(jié)果更加準(zhǔn)確以到達(dá)更高的性能。所以將這個(gè)任務(wù)交給開(kāi)發(fā)者本身其實(shí)也是非常合理的。最好的內(nèi)存分配者就是你自己!! Memory usage傳統(tǒng)圖形API還有一個(gè)問(wèn)題存在,那就是傳統(tǒng)圖形API的邏輯資源與支持它的物理內(nèi)存緊密結(jié)合起來(lái)。這在使用上非常簡(jiǎn)單,但意味著很多中間存儲(chǔ)(例如FrameBuffer的Attachment)只用于一個(gè)幀內(nèi)。而Vulkan將資源的概念如Image或者Buffer與支持它的物理內(nèi)存分開(kāi)。并且由于開(kāi)發(fā)者可以精準(zhǔn)的控制資源堆內(nèi)存的分配??梢酝ㄟ^(guò)使用Memory Alias能力(在同一堆空間中創(chuàng)建不同的資源,對(duì)一塊內(nèi)存進(jìn)行復(fù)用),這使得在渲染過(guò)程中的不同時(shí)間節(jié)點(diǎn)上為多個(gè)不同的資源重復(fù)使用相同的物理內(nèi)存,從而更有效的使用有限的 GPU 內(nèi)存資源。 Shader Compilation傳統(tǒng)圖形API(OpenGL/ES) 則采用了另一種方式,將 Shader 編譯交給驅(qū)動(dòng)程序完成,這樣就會(huì)導(dǎo)致每次編譯時(shí)帶來(lái)的不必要的語(yǔ)法分析以及校驗(yàn)開(kāi)銷。但是現(xiàn)代圖形API全部都提供了它們的預(yù)編譯Shader方案。Vulkan提出了SPIR-V 標(biāo)準(zhǔn)并且可將 GLSL/HLSL 編譯為SPIR-V格式。使用字節(jié)碼格式的優(yōu)勢(shì)在于 GPU 供應(yīng)商編寫的將Shader代碼轉(zhuǎn)換為本機(jī)代碼的編譯器的復(fù)雜性要低得多。過(guò)去表明對(duì)于像 GLSL 這樣的人類可讀語(yǔ)法,一些 GPU 供應(yīng)商對(duì)標(biāo)準(zhǔn)的解釋相當(dāng)靈活可能在不同的平臺(tái)兼容性會(huì)有問(wèn)題。使用像 SPIR-V 這樣的簡(jiǎn)單字節(jié)碼格式有望避免該問(wèn)題,并且能夠減少傳統(tǒng)圖形API當(dāng)中的檢驗(yàn)開(kāi)銷等等具有更好的性能。 還有一個(gè)問(wèn)題還是上面提到的關(guān)于Shader編譯時(shí)機(jī)的改變(Shader編譯是一個(gè)相當(dāng)?shù)暮臅r(shí)的操作),在傳統(tǒng)圖形API中對(duì)于這個(gè)Shader編譯是沒(méi)有明確的時(shí)機(jī)的,完全是驅(qū)動(dòng)程序決定。但是在Vulkan當(dāng)中Shader編譯的明確時(shí)機(jī)就是在創(chuàng)建Pipeline時(shí)。這可以讓開(kāi)發(fā)者更加明確的控制Shader編譯時(shí)機(jī)。 能從Vulkan獲得什么?Vulkan 是一個(gè)相當(dāng)?shù)讓覣PI,它為開(kāi)發(fā)者開(kāi)放了更多的能力,但作為代價(jià),它也將很多責(zé)任推給了開(kāi)發(fā)者以正確的方式做事。在使用Vulkan之前值得考慮一下它帶來(lái)的好處以及您必須付出的代價(jià)。并不總是適合每個(gè)項(xiàng)目的正確選擇。使用 Vulkan 時(shí)要記住的最重要的一點(diǎn)是,它不一定會(huì)給您帶來(lái)性能提升。 使用的GPU 硬件是相同的,Vulkan能夠提供的渲染功能幾乎與傳統(tǒng)的圖形API相同。如果您的應(yīng)用程序受到 GPU 渲染性能的限制,那么 Vulkan 不太可能為您提供更多性能上的提升。 優(yōu)點(diǎn)Vulkan 帶來(lái)的最大優(yōu)勢(shì)是減少了驅(qū)動(dòng)程序和應(yīng)用程序渲染邏輯中的CPU側(cè)負(fù)載。這是通過(guò)簡(jiǎn)化 API 接口和對(duì)于多線程支持來(lái)實(shí)現(xiàn)的。這可以提高 遇到CPU側(cè)瓶頸的應(yīng)用程序的性能,并提高整體系統(tǒng)能效。 第二個(gè)優(yōu)點(diǎn)是由于中間內(nèi)存資源的幀內(nèi)回收,減少了應(yīng)用程序的內(nèi)存占用。雖然這在高端設(shè)備中很少出現(xiàn)問(wèn)題,但它可以在帶有較小 RAM 的大眾市場(chǎng)設(shè)備中有更好的效果。 缺點(diǎn)Vulkan的優(yōu)點(diǎn)有時(shí)同樣會(huì)變成缺點(diǎn),雖然減少了CPU側(cè)的負(fù)載。但是需要去管理在傳統(tǒng)圖形API當(dāng)中被隱藏起來(lái)的細(xì)節(jié),包括內(nèi)存分配、同步管理等等。開(kāi)發(fā)者需要負(fù)責(zé)的東西更多了。雖然這可以實(shí)現(xiàn)高度的控制,但是同樣在不當(dāng)?shù)木幊滔伦兂梢环N負(fù)優(yōu)化。還值得注意的是較薄的抽象級(jí)別意味著 Vulkan 可以對(duì)底層 GPU 硬件的差異更加敏感從而降低了一定的可移植性,因?yàn)轵?qū)動(dòng)程序無(wú)法幫助隱藏硬件差異。 挑戰(zhàn)可以看到現(xiàn)代圖形 API 是更加顯式的控制能力并且對(duì)于開(kāi)發(fā)者相當(dāng)?shù)耐该?,最終目的是最大化提高渲染性能。但是如果要使用現(xiàn)代圖形API想壓榨出所有的性能也并非易事。簡(jiǎn)單的移植現(xiàn)有的程序有時(shí)候并不能夠提高效率有時(shí)候反而降低性能(比如錯(cuò)誤的同步反而會(huì)導(dǎo)致GPU停頓降低性能)。這也是Vulkan給開(kāi)發(fā)者提出的難題。 首先是開(kāi)發(fā)者思維的轉(zhuǎn)變,沒(méi)有驅(qū)動(dòng)程序?yàn)樵蹅冏瞿敲炊嗍虑槔?。?duì)于開(kāi)發(fā)者的要求也更高。但是這一切的付出都值得。在Vulkan和現(xiàn)代GPU的發(fā)展的情況下,才能夠發(fā)揮出更大的價(jià)值,挖掘出更多的可能。從上面我可以看到Vulkan將之前在驅(qū)動(dòng)程序?qū)用婀ぷ髁拷桓督o開(kāi)發(fā)者去完成,也就是某種意思上開(kāi)發(fā)者承擔(dān)了部分驅(qū)動(dòng)程序的開(kāi)發(fā),那么這些工作量也就是挑戰(zhàn)所在。
還有很多其他的方面的比如Command Buffer如何復(fù)雜均衡以及RenderPass的各種參數(shù)設(shè)置等等。這些對(duì)于開(kāi)發(fā)者來(lái)說(shuō)都是不小的挑戰(zhàn)。 總結(jié)Vulkan 是一個(gè)底層 API,它賦予開(kāi)發(fā)者高度的控制權(quán)和責(zé)任感,并通過(guò)非常低的 CPU 開(kāi)銷通過(guò)提供對(duì) GPU 硬件和圖形資源的訪問(wèn)。很好地使用它的應(yīng)用程序可以受益于減少 CPU 負(fù)載和內(nèi)存占用,減少因更厚的驅(qū)動(dòng)程序抽象對(duì)應(yīng)用程序進(jìn)行第二次猜測(cè)而導(dǎo)致的故障。需要注意的是Vulkan 很少提升 GPU 渲染性能,畢竟使用GPU硬件是相同的。如果你的程序的瓶頸是在GPU或者項(xiàng)目在CPU側(cè)很難多線程并行化的話,那么Vulkan也很難幫助你在效率上有所提升。 并且從傳統(tǒng)的圖形API移植到現(xiàn)代圖形API的成本也是不小的,收益和工作量的對(duì)比如下所示: 可以用下面這一張圖,來(lái)表明什么時(shí)機(jī)適合使用Vulkan Referencesdeveloper.download.nvidia.com/gameworks/events/GDC2016/Vulkan_Essentials_GDC16github.com/KhronosGroup/VulkanGuide/blob/master/chapters/what_is_vulkan.adocs://github.com/KhronosGroup |
|
來(lái)自: 開(kāi)花結(jié)果 > 《Display》