[譯]Vulkan教程(02)概況html
這是我翻譯(https://vulkan-tutorial.com)上的Vulkan教程的第2篇。程序員
This chapter will start off with an introduction of Vulkan and the problems it addresses. After that we're going to look at the ingredients that are required for the first triangle. This will give you a big picture to place each of the subsequent chapters in. We will conclude by covering the structure of the Vulkan API and the general usage patterns.編程
本章首先將介紹Vulkan和它解決的問題。而後咱們要看一下繪製第一個三角形須要的材料。這會給你一個全局觀念,以便(在頭腦中)安放後續章節。最後,咱們將總結Vulkan API的結構和通常的使用模式。windows
Just like the previous graphics APIs, Vulkan is designed as a cross-platform abstraction over GPUs. The problem with most of these APIs is that the era in which they were designed featured graphics hardware that was mostly limited to configurable fixed functionality. Programmers had to provide the vertex data in a standard format and were at the mercy of the GPU manufacturers with regards to lighting and shading options.緩存
和以前的圖形API同樣,Vulkan被設計爲一個跨平臺的對GPU的抽象。在以前的API被設計的年代,它們都是針對當時的圖形硬件而設計了可配置的固定功能,這是它們的問題。程序員不得不用標準的格式提供頂點數據,聽命於GPU廠商提供的光照和着色選項。多線程
As graphics card architectures matured, they started offering more and more programmable functionality. All this new functionality had to be integrated with the existing APIs somehow. This resulted in less than ideal abstractions and a lot of guesswork on the graphics driver side to map the programmer's intent to the modern graphics architectures. That's why there are so many driver updates for improving the performance in games, sometimes by significant margins. Because of the complexity of these drivers, application developers also need to deal with inconsistencies between vendors, like the syntax that is accepted for shaders. Aside from these new features, the past decade also saw an influx of mobile devices with powerful graphics hardware. These mobile GPUs have different architectures based on their energy and space requirements. One such example is tiled rendering, which would benefit from improved performance by offering the programmer more control over this functionality. Another limitation originating from the age of these APIs is limited multi-threading support, which can result in a bottleneck on the CPU side.架構
隨着圖形卡架構的成熟,它們開始提供愈來愈多的可編程功能。全部的這些新功能都必須以某種方式加入現有的API中。這致使了抽象不夠理想,圖形driver端(在將程序員的意圖映射到現代圖形架構時)要作不少判斷。這就是驅動頻繁更新以提高遊戲性能(有時提高十分明顯)的緣由。因爲driver的複雜性,app開發者也須要處理不一樣廠商之間的不一致問題,例如shader的語法。除了這些新特性,過去幾十年也涌現了帶有強大圖形硬件的移動設備。因爲能量和空間限制,這些移動設備的GPU有不一樣的架構。一個例子是排列式成像,它經過提供給程序員更多的控制權獲得了更高的性能。這些API的年紀帶來的另外一個限制它們是對多線程的支持有限,這會致使CPU端的瓶頸。併發
Vulkan solves these problems by being designed from scratch for modern graphics architectures. It reduces driver overhead by allowing programmers to clearly specify their intent using a more verbose API, and allows multiple threads to create and submit commands in parallel. It reduces inconsistencies in shader compilation by switching to a standardized byte code format with a single compiler. Lastly, it acknowledges the general purpose processing capabilities of modern graphics cards by unifying the graphics and compute functionality into a single API.app
Vulkan是從零開始爲現代圖形架構設計的,它解決了上述問題。它容許程序員用一個冗繁得多的API,清楚地代表他們的意圖,還容許在多線程中併發地建立和提交命令,從而減小了driver開銷。它用一個惟一的編譯器獲得標準的字節碼,從而減小了在shader編譯方面的不一致性。最後,它用統一的圖形和計算功能API來調用現代圖形卡的處理能力。(譯者注:Vulkan既能夠用於圖形渲染,又能夠用於非圖形計算。)less
We'll now look at an overview of all the steps it takes to render a triangle in a well-behaved Vulkan program. All of the concepts introduced here will be elaborated on in the next chapters. This is just to give you a big picture to relate all of the individual components to.
如今咱們將概覽在一個表現良好的Vulkan程序中渲染一個三角形所需的全部步驟。這裏介紹的全部概念都將在接下來的章節中詳述。這裏只是給你個全局概念,讓你將各個獨立的組件關聯起來。
A Vulkan application starts by setting up the Vulkan API through a VkInstance
. An instance is created by describing your application and any API extensions you will be using. After creating the instance, you can query for Vulkan supported hardware and select one or more VkPhysicalDevice
s to use for operations. You can query for properties like VRAM size and device capabilities to select desired devices, for example to prefer using dedicated graphics cards.
Vulkan應用程序開始時要經過一個VkInstance來設置Vulkan API。一個instance經過(描述你的app和你將要使用的全部API擴展)來建立。建立完instance後,你能夠查詢Vulkan支持的硬件,選擇一個或多個VkPhysicalDevice,用於後續操做。你能夠查詢VRAM大小、設備功能等,用以選擇想要的設備,例如選擇某種專用圖形卡。
After selecting the right hardware device to use, you need to create a VkDevice
(logical device), where you describe more specifically which VkPhysicalDeviceFeatures
you will be using, like multi viewport rendering and 64 bit floats. You also need to specify which queue families you would like to use. Most operations performed with Vulkan, like draw commands and memory operations, are asynchronously executed by submitting them to a VkQueue
. Queues are allocated from queue families, where each queue family supports a specific set of operations in its queues. For example, there could be separate queue families for graphics, compute and memory transfer operations. The availability of queue families could also be used as a distinguishing factor in physical device selection. It is possible for a device with Vulkan support to not offer any graphics functionality, however all graphics cards with Vulkan support today will generally support all queue operations that we're interested in.
選擇了正確的硬件設備後,你須要建立一個VkDevice (邏輯設備),它描述了你要使用哪些VkPhysicalDeviceFeatures ,例如多視口渲染和64位浮點數。你也須要標明你想使用哪一個queue家族。Vulkan實施的大多數操做,例如繪製命令和內存操做,都是經過提交它們到一個VkQueue,來異步執行的。Queue是從queue家族分配的,每一個queue家族裏的queue都支持特定的一些操做(這些操做構成一個集合)。例如,有的queue家族支持圖形操做,有的支持計算操做,有的支持內存轉移操做。Queue家族的能力也能夠用於選擇物理設備的區分因素。可能存在徹底不支持圖形功能的Vulkan設備,可是當今全部的Vulkan圖形卡通常都支持咱們感興趣的全部queue操做。
Unless you're only interested in offscreen rendering, you will need to create a window to present rendered images to. Windows can be created with the native platform APIs or libraries like GLFW and SDL. We will be using GLFW in this tutorial, but more about that in the next chapter.
你將須要建立一個窗口來呈現渲染的圖像,除非你只對離屏渲染感興趣。窗口能夠用本地平臺API或GLFW和SDL這樣的庫建立。本教程中咱們將使用GLFW,可是下一章再細說。
We need two more components to actually render to a window: a window surface (VkSurfaceKHR
) and a swap chain (VkSwapchainKHR
). Note the KHR
postfix, which means that these objects are part of a Vulkan extension. The Vulkan API itself is completely platform agnostic, which is why we need to use the standardized WSI (Window System Interface) extension to interact with the window manager. The surface is a cross-platform abstraction over windows to render to and is generally instantiated by providing a reference to the native window handle, for example HWND
on Windows. Luckily, the GLFW library has a built-in function to deal with the platform specific details of this.
咱們還須要2個組件來渲染到窗口:一個窗口surface(VkSurfaceKHR)和一個交換鏈(VkSwapchainKHR)。注意,後綴KHR 意思是這些對象是Vulkan擴展的一部分。Vulkan API是徹底的平臺不可知論者,這就是咱們須要用標準化WSI(窗口系統接口)擴展與窗口管理器交互的緣由。Surface是對可渲染窗口的跨平臺抽象,通常經過提供一個本地句柄的方式來實例化,例如在Windows上提供的句柄是HWND 。幸運的是,GLFW庫有個內建函數處理平臺相關的細節。
The swap chain is a collection of render targets. Its basic purpose is to ensure that the image that we're currently rendering to is different from the one that is currently on the screen. This is important to make sure that only complete images are shown. Every time we want to draw a frame we have to ask the swap chain to provide us with an image to render to. When we've finished drawing a frame, the image is returned to the swap chain for it to be presented to the screen at some point. The number of render targets and conditions for presenting finished images to the screen depends on the present mode. Common present modes are double buffering (vsync) and triple buffering. We'll look into these in the swap chain creation chapter.
交換鏈是渲染目標的集合。它的基本目的是確保當前正在渲染的image(圖像)與當前正在呈現到屏幕的,不是同一個。爲確保只有完整的image被呈現,這很重要。每次咱們想繪製一幀時,咱們不得不請求交換鏈提供給咱們一個用於渲染的image。當咱們完成了繪製這一幀,這個image就返回到交換鏈,準備在某時呈現到屏幕。渲染目標的數量,呈現image到屏幕的條件,都依賴於呈現模式。常見的呈現模式是雙緩存(垂直同步)和三緩存。咱們將在交換鏈建立章節詳述這些。
Some platforms allow you to render directly to a display without interacting with any window manager through the VK_KHR_display
and VK_KHR_display_swapchain
extensions. These allow you to create a surface that represents the entire screen and could be used to implement your own window manager, for example.
有的平臺容許你直接渲染到顯示器,無需與窗口管理器交互,只要使用VK_KHR_display 和VK_KHR_display_swapchain 擴展便可。這樣你就能夠建立一個表明整個顯示器區域的surface,用其實現本身的窗口管理器。
To draw to an image acquired from the swap chain, we have to wrap it into a VkImageView
and VkFramebuffer
. An image view references a specific part of an image to be used, and a framebuffer references image views that are to be used for color, depth and stencil targets. Because there could be many different images in the swap chain, we'll preemptively create an image view and framebuffer for each of them and select the right one at draw time.
爲了在一個從交換鏈上獲得的image上繪製,咱們不得不將其封裝到VkImageView 和VkFramebuffer。一個image視圖指定image的哪一部分被使用,一個幀緩存指定image視圖是被用做顏色、深度仍是模板目標。由於交換鏈上可能有不少不一樣的image,咱們將先發制人地爲每一個image建立一個image視圖和幀緩存,而後在繪製時選擇正確的那個。
Render passes in Vulkan describe the type of images that are used during rendering operations, how they will be used, and how their contents should be treated. In our initial triangle rendering application, we'll tell Vulkan that we will use a single image as color target and that we want it to be cleared to a solid color right before the drawing operation. Whereas a render pass only describes the type of images, a VkFramebuffer
actually binds specific images to these slots.
Vulkan中的渲染pass描述用於渲染操做的image類型,它們將被如何使用,它們的內容將被用於何處。在咱們最初的渲染三角形app中,咱們會告訴Vulkan咱們將用一個image做爲顏色目標,將其清空爲一個固定顏色,以後再執行繪製操做。一個渲染pass只描述image的類型,但VkFramebuffer 纔會實際綁定到具體的image。
The graphics pipeline in Vulkan is set up by creating a VkPipeline
object. It describes the configurable state of the graphics card, like the viewport size and depth buffer operation and the programmable state using VkShaderModule
objects. The VkShaderModule
objects are created from shader byte code. The driver also needs to know which render targets will be used in the pipeline, which we specify by referencing the render pass.
Vulkan中的圖形管道經過建立VkPipeline 對象來構建。它描述了圖形卡的可配置的狀態,例如視口大小、深度緩存操做和VkShaderModule 對象的可編程狀態。VkShaderModule 對象從shader字節碼建立。Driver也須要知道哪一個渲染目標會被用於管道中,這一點,咱們經過引用渲染pass來標明。
One of the most distinctive features of Vulkan compared to existing APIs, is that almost all configuration of the graphics pipeline needs to be set in advance. That means that if you want to switch to a different shader or slightly change your vertex layout, then you need to entirely recreate the graphics pipeline. That means that you will have to create many VkPipeline
objects in advance for all the different combinations you need for your rendering operations. Only some basic configuration, like viewport size and clear color, can be changed dynamically. All of the state also needs to be described explicitly, there is no default color blend state, for example.
Vulkan與原有API區別最大的特性之一是,幾乎全部的圖形管道配置工做都須要提早作好。意思是,若是你想切換到不一樣的shader或稍微改變你的頂點佈局,那麼你須要整個重建圖形管道。這意味着你不得不提早建立不少VkPipeline 對象,以用於不一樣的渲染操做的組合。只有一些基本的配置,例如視口大小和清空顏色,能夠被動態地改變。全部狀態都須要被顯式地描述纔會有,例如,不存在默認的顏色混合狀態。
The good news is that because you're doing the equivalent of ahead-of-time compilation versus just-in-time compilation, there are more optimization opportunities for the driver and runtime performance is more predictable, because large state changes like switching to a different graphics pipeline are made very explicit.
好消息是,因爲你作的提早編譯(而不是即時編譯),driver有更多的優化機會,運行時性能也更加可預測,由於重量級狀態改變(例如切換到另外一個圖形管道)被顯式地指出了。
As mentioned earlier, many of the operations in Vulkan that we want to execute, like drawing operations, need to be submitted to a queue. These operations first need to be recorded into a VkCommandBuffer
before they can be submitted. These command buffers are allocated from a VkCommandPool
that is associated with a specific queue family. To draw a simple triangle, we need to record a command buffer with the following operations:
如前所述,在Vulkan中,咱們想要執行的不少操做,例如繪製操做,須要被提交到一個queue裏。在提交前,這些操做首先須要被記錄到VkCommandBuffer 中。這些命令緩存是從一個命令池VkCommandPool 中申請的,命令池與一個特定的queue家族關聯。爲了繪製一個三角形,咱們須要記錄一個有下述操做的命令緩存:
Because the image in the framebuffer depends on which specific image the swap chain will give us, we need to record a command buffer for each possible image and select the right one at draw time. The alternative would be to record the command buffer again every frame, which is not as efficient.
由於幀緩存中的image依賴於交換鏈給咱們哪一個image,咱們須要對每一個可能的image記錄一個命令緩存,並在繪製時選擇正確的那個。另外一個方式是,每一幀都記錄一次命令緩存,但這就不效率了。
Now that the drawing commands have been wrapped into a command buffer, the main loop is quite straightforward. We first acquire an image from the swap chain with vkAcquireNextImageKHR
. We can then select the appropriate command buffer for that image and execute it with vkQueueSubmit
. Finally, we return the image to the swap chain for presentation to the screen with vkQueuePresentKHR
.
既然繪製命令已經被封裝到命令緩存裏,主循環就至關直截了當了。咱們首先用函數vkAcquireNextImageKHR從交換鏈請求一個image。而後咱們就能夠爲此image選擇恰當的命令緩存,並用函數vkQueueSubmit執行它。最後,咱們用函數vkQueuePresentKHR將image返回到交換鏈,以使之被呈現到屏幕。
Operations that are submitted to queues are executed asynchronously. Therefore we have to use synchronization objects like semaphores to ensure a correct order of execution. Execution of the draw command buffer must be set up to wait on image acquisition to finish, otherwise it may occur that we start rendering to an image that is still being read for presentation on the screen. The vkQueuePresentKHR
call in turn needs to wait for rendering to be finished, for which we'll use a second semaphore that is signaled after rendering completes.
提交到queue的操做是被異步執行的。所以咱們不得不使用同步對象(例如信號)來確保執行的正確順序。必須等待image的請求結束後,才能執行繪製命令緩存的操做。不然,可能發生咱們開始渲染到image了可是image還在被用於呈現到屏幕上的狀況。依序,調用函數vkQueuePresentKHR 前須要等待渲染操做完成,爲此咱們將用另外一個信號對象(在渲染完成後發信號)。
This whirlwind tour should give you a basic understanding of the work ahead for drawing the first triangle. A real-world program contains more steps, like allocating vertex buffers, creating uniform buffers and uploading texture images that will be covered in subsequent chapters, but we'll start simple because Vulkan has enough of a steep learning curve as it is. Note that we'll cheat a bit by initially embedding the vertex coordinates in the vertex shader instead of using a vertex buffer. That's because managing vertex buffers requires some familiarity with command buffers first.
此次旋風之旅應該給你一個基礎的理解,知道繪製第一個三角形以前的工做有哪些。實際的app包含更多的步驟,例如申請頂點緩存、建立uniform緩存和上傳texture image,這些會在後續章節介紹。但咱們從簡單的開始,由於Vulkan的學習曲線已經很陡峭了。注意,咱們將耍個滑頭,在vertex shader中初始化一些內嵌的頂點座標,而非使用頂點緩存。這是由於管理頂點緩存的前提條件之一是熟悉命令緩存。
So in short, to draw the first triangle we need to:
VkInstance
VkPhysicalDevice
)VkDevice
and VkQueue
for drawing and presentationVkImageView
簡單來講,爲了繪製第一個三角形,咱們須要:
It's a lot of steps, but the purpose of each individual step will be made very simple and clear in the upcoming chapters. If you're confused about the relation of a single step compared to the whole program, you should refer back to this chapter.
步驟不少啊,可是每一個獨立步驟的目的將會十分簡單清楚地展示在後續章節中。若是你不明白某一步驟在整個程序中的做用,你就應該重讀本章。
This chapter will conclude with a short overview of how the Vulkan API is structured at a lower level.
本章最後將簡要介紹Vulkan API在低層上的結構。
All of the Vulkan functions, enumerations and structs are defined in the vulkan.h
header, which is included in the Vulkan SDK developed by LunarG. We'll look into installing this SDK in the next chapter.
全部的Vulkan函數、枚舉和結構體都在頭文件vulkan.h 中定義,由LunarG開發的Vulkan SDK裏有這個文件。
Functions have a lower case vk
prefix, types like enumerations and structs have a Vk
prefix and enumeration values have a VK_
prefix. The API heavily uses structs to provide parameters to functions. For example, object creation generally follows this pattern:
函數帶有小寫的vk 前綴,枚舉等類型和結構體有Vk 前綴,枚舉值有VK_ 前綴。這個API大量使用結構體作爲函數的參數。例如,建立對象的過程廣泛遵循這樣的模式:
1 VkXXXCreateInfo createInfo = {}; 2 createInfo.sType = VK_STRUCTURE_TYPE_XXX_CREATE_INFO; 3 createInfo.pNext = nullptr; 4 createInfo.foo = ...; 5 createInfo.bar = ...; 6 7 VkXXX object; 8 if (vkCreateXXX(&createInfo, nullptr, &object) != VK_SUCCESS) { 9 std::cerr << "failed to create object" << std::endl; 10 return false; 11 }
Many structures in Vulkan require you to explicitly specify the type of structure in the sType
member. The pNext
member can point to an extension structure and will always be nullptr
in this tutorial. Functions that create or destroy an object will have a VkAllocationCallbacks
parameter that allows you to use a custom allocator for driver memory, which will also be left nullptr
in this tutorial.
Vulkan中的許多結構體要求你顯式地在成員sType標明結構體的類型。成員pNext 可能指向一個擴展結構體,在本教程中它將始終爲nullptr 。
Almost all functions return a VkResult
that is either VK_SUCCESS
or an error code. The specification describes which error codes each function can return and what they mean.
幾乎全部函數都返回一個VkResult ,它要麼是VK_SUCCESS ,要麼是一個錯誤碼。說明書裏描述了每一個函數可能返回哪些錯誤碼及其含義。
As mentioned earlier, Vulkan is designed for high performance and low driver overhead. Therefore it will include very limited error checking and debugging capabilities by default. The driver will often crash instead of returning an error code if you do something wrong, or worse, it will appear to work on your graphics card and completely fail on others.
如前所述,設計Vulkan是爲了更高的性能和更低的driver開銷。所以它默認只有頗有限的錯誤檢查和調試能力。若是你作錯了什麼,driver會常常崩潰,而不是返回錯誤碼,甚至更糟,它在你的圖形卡上能工做可是在其餘的圖形卡上就徹底不行。
Vulkan allows you to enable extensive checks through a feature known as validation layers. Validation layers are pieces of code that can be inserted between the API and the graphics driver to do things like running extra checks on function parameters and tracking memory management problems. The nice thing is that you can enable them during development and then completely disable them when releasing your application for zero overhead. Anyone can write their own validation layers, but the Vulkan SDK by LunarG provides a standard set of validation layers that we'll be using in this tutorial. You also need to register a callback function to receive debug messages from the layers.
Vulkan容許你經過一個被稱爲驗證層的特性來啓用額外的檢查。驗證層是一段代碼,能夠插進API和圖形驅動之間,作一些例如額外檢查函數參數和追蹤內存管理問題的事。好處是你能夠在開發期間啓用它,在發佈app時完全禁用它,以消除此開銷。任何人均可以寫本身的驗證層,可是LunarG開發的Vulkan SDK提供了一個驗證層的標準集,咱們在本教程中就用這個。你還須要註冊一個回調函數來接收這些層送來的調試信息。
Because Vulkan is so explicit about every operation and the validation layers are so extensive, it can actually be a lot easier to find out why your screen is black compared to OpenGL and Direct3D!
由於Vulkan的每一個操做都是如此的顯式,驗證層又是如此的可擴展,想要找到爲何你的屏幕是一片漆黑,這要比OpenGL和Direct3D簡單得多!
There's only one more step before we'll start writing code and that's setting up the development environment.
距離開始寫代碼還差一步,那就是配置開發環境。
原文出處:https://www.cnblogs.com/bitzhuwei/p/Vulkan-Tutorial-02-Overview.html