Vulkan是Khronos Group(OpenGL標準的維護組織)開發的一個新API,它提供了對現代顯卡的一個更好的抽象,與OpenGL和Direct3D等現有api相比,Vulkan可以更詳細的向顯卡描述你的應用程序打算做什么,從而可以獲得更好的性能和更小的驅動開銷。Vulkan的設計理念與Direct3D 12和Metal基本類似,但Vulkan作為OpenGL的替代者,它設計之初就是為了跨平臺實現的,可以同時在Windows、Linux和Android開發。甚至在Mac OS系統上,Khronos也提供了Vulkan的SDK,雖然這個SDK底層其實是使用MoltenVK實現的。
MoltenVK實際上是一個將Vulkan API映射到Metal API的一個框架,Vulkan這里只是相當于一個抽象層。
然而,為了得到一個更好的性能,Vulkan引入了一個非常冗余的API。相比于OpenGL驅動幫我們做了大量的工作,Vulkan與圖像api相關的每一個細節,都需要從頭設置,包括初始幀緩沖區的創建與緩沖、紋理內存的管理等等。因此,哪怕只畫一個三角形,我們都要寫數倍于OpenGL的代碼。
而Google在Android 7.0后提供了對Vulkan的支持,并且提供了一系列工具鏈與Validation Layers(后面會進行說明)。在Android Studio中,只要將Shader代碼放在src/main/shaders文件夾下面,項目編譯時會自動被編譯成.spv字節碼,可以作為assets使用。
由于Vulkan的使用非常冗長,這篇文章將主要介紹Vulkan API的一般使用模式以及畫一個三角形所需要的基本元素。
Vulkan畫三角形的各個步驟
1. Instance and physical device selection
Vulkan程序需要創建一個VkInstance來啟用vulkan API.在創建Instance時你需要準確的描述你的應用程序的一些屬性,以及你需要使用的各種API。在創建Instance之后,你需要查詢硬件對vulkan的支持,并選擇一個或者多個VkPhysicalDevices。你會查詢各種硬件參數,比如顯存和顯卡兼容性,來選取最理想的設備。舉個例子,相比集顯,咱們更喜歡獨顯。
2. Logical device and queue families
在決定使用哪塊顯卡之后,你需要創建一個VkDevice (logical device),在創建過程中你還要更詳細的描述你用到的硬件特性(VkPhysicalDeviceFeatures),比如multi viewport rendering(該特性VR渲染非常有用)和64位浮點支持(科學計算和HDR都需要),你還需要制定你要使用的queue families。
大部分Vulkan操作, 比如繪制命令和內存/顯存操作, 都需要通過提交到VkQueue之后異步執行。Queue是從queue family分配的,每個queue family中的Queue只支持某個特定集合的操作。舉個例子,對于顯卡,執行圖形渲染、并行計算和內存交換可能是不同的queue family。在選擇physical device時queue family的支持也是重要的參考之一。只支持科學計算不支持圖形渲染的顯卡有可能存在(事實上繪圖相關的queue family是vulkan的擴張而非核心功能),但是這年頭支持vulkan但不支持繪圖的設備,其實也不多。
3. Window surface and swap chain
除非你不想顯示圖形(比如你只想離屏渲染),不然你還是需要創建一個窗口來顯示的。你可以用各個平臺native的API(win32,xlib,xcb,mir,wayland),或者GLFW、SDL等申請window。如果真的想渲染到一個窗口,還需要兩樣東西: window surface (VkSurfaceKHR) 和 swap chain (VkSwapChainKHR)。注意 KHR 后綴,有openGL開發經驗的同學可能清楚,這個后綴表示一個KHR擴展。Vulkan API 是平臺無關的, 所以需要一個標準化的 WSI (Window System Interface) 擴展來和窗口系統進行交互。Surface是一個window渲染目標的抽象,創建時需要一個native窗口的句柄(和openGL完全一樣)。Swap chain是渲染一系列渲染目標(render target)的集合。他本來是保證我們渲染的image和屏幕上顯示的image不是同一個(openGL雙緩沖模型,目的是減少渲染時屏幕閃爍)。除了經典的雙緩沖模型,還有三緩沖模型等。
4. Image views and framebuffers
在從swap chain獲得image之后,我們應該繪制它。為了繪制image,我們需要把這個image用 VkImageView 以及 VkFramebuffer進行包裝。VkImageView 指向被使用的 image ,而 VkFramebuffer 指向具體作為顏色、模板、深度目標的VkImageView。Swap chain中可能存在很多iamge,每個都需要創建image view還有framebuffer。在渲染時,我們需要選擇正確的image進行繪制。
5. Render passes
Vulkan中的Render Pass描述了渲染過程中的image類型以及這些image包括他們的內容會如何被使用,在我們這個Hello Triangle程序中,我們只使用一個image作為顏色目標,并且在渲染前這個image的類型會被清除成一個固定的顏色。Render Pass只描述了image的類型,而VkFramebuffer才是真正綁定該image的對象。
6. Graphics pipeline
Vulkan中通過創建VkPipeline對象來創建graphics pipeline,它用來描述顯卡各個渲染階段的參數,比如viewport的長寬、如何使用深度緩沖,以及用VkShaderModule詳細描述的可編程管線的狀態。Vulkan和其他圖形API有個顯著區別,就是的幾乎所有配置都需要提前創建。即便是更換一個shader或者改變定點參數的布局(vertex layout),你都需要完全從新graphics pipeline創建一個graphics pipeline。這也意味著你需要創建大量的VkPipeline對象,來覆蓋你渲染過程中的各種graphics pipeline變化,只有改變viewport或者改變clear color不需要重新生成graphics pipeline。但是正因為渲染管線各階段都提前準備了,而不是運行時生成,驅動可以更好的執行優化。
7. Command pools and command buffers
前文中已經提到了,Vulkan中的各種操作,都需要靠提交到一個命令隊列(queue)的方式進行異步執行。而在提交到命令隊列之前,需要在VkCommandBuffer中進行記錄。命令緩沖和一個VkCommandPool關聯,而VkCommandPool又和queue family關聯。即便只畫一個簡單的三角形,我們也需要生成如下的command buffer:
Begin the render pass Bind the graphics pipeline Draw 3 vertices End the render pass 因為framebuffer中的image由swap chain決定,每個可能的iamge都需要一個command buffer,這就需要創建大量的command buffer。也可以每幀重新生成command buffer,但是效率會低很多。
8. Main loop
在command buffer準備好了之后,主循環就簡單多了。我們先使用vkAcquireNextImageKHR方法得到一個image。然后選擇合適的command buffer再使用vkQueueSubmit執行。最后使用vkQueuePresentKHR方法向swap chain傳遞我們準備在屏幕上顯示的image。另外需要注意的是提交到命令隊列的命令是異步的,因此保證運行順序,就是開發者的工作了。vulkan提供了用來同步的對象,比如semaphore,fence等。我們可以使用這些同步對象來保證運行的正確順序。
總結
總而言之,畫三角需要如下的步驟:
創建 VkInstance
創建 VkPhysicalDevice
創建 VkDevice 以及 VkQueue
創建 window, window surface 以及 swap chain
使用 VkImageView 包裝swap chain中的images
創建 render pass
創建 framebuffers
創建 graphics pipeline
為每個可能用到的image創建command buffer,并記錄draw commands
通過獲取image,向image繪制,提交到swap chain的方式來繪制一幀
Vulkan Layers
Vulkan作為高性能API,為了降低驅動開銷,只提供了非常有限的錯誤檢查。如果默認情況下,它只包含非常有限的錯誤檢查和調試功能。如果發生了錯誤,驅動會直接崩潰,而不是返回錯誤信息,或者顯示異常等。
Vulkan允許您通過一個名為Validation Layer的特性進行通用的檢查。Validation Layer是可以插入到API和圖形驅動程序之間的代碼片段,用于執行額外的函數參數檢查和跟蹤內存管理問題。好處是,您可以在開發過程中啟用它們,然后在釋放應用程序時完全禁用它們,而開銷為零。任何人都可以編寫自己的驗證層,但是LunarG的Vulkan SDK提供了一組標準的驗證層,另外還需要注冊一個回調函數來接收來自Validation Layer的調試消息。
因為Vulkan實際上對于每一個步驟是非常明確的,所以相比于OpenGL,我們可以更快速的找出出錯的地方。
而Google也提供了一些Validation Layers幫助我們做這些事情,如果要在項目中使用它們,只要修改gradle的構建,或者將二進制文件手動的添加到項目的JNI庫目錄里,這些so可以在ndk的以下目錄找到:
${your-ndk-dir}/sources/third_party/vulkan/src/build-android/jniLibs
sourceSets {
main {
jniLibs {
srcDir "${your-ndk-dir}/sources/third_party/vulkan/src/build-android/jniLibs"
}
}
}
-
Android
+關注
關注
12文章
3936瀏覽量
127404 -
Linux
+關注
關注
87文章
11304瀏覽量
209502 -
API
+關注
關注
2文章
1501瀏覽量
62018
原文標題:Vulkan入門流程
文章出處:【微信號:Imgtec,微信公眾號:Imagination Tech】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論