操作系統(tǒng):Windows8.1

顯卡:Nivida GTX965M

開發(fā)工具:Visual Studio 2017


到目前為止,我們了解到Vulkan是一個與平臺特性無關(guān)聯(lián)的API集合。它不能直接與窗口系統(tǒng)進行交互。為了將渲染結(jié)果呈現(xiàn)到屏幕,需要建立Vulkan與窗體系統(tǒng)之間的連接,我們需要使用WSI(窗體系統(tǒng)集成)擴展。在本小節(jié)中,我們將討論第一個,即VK_KHR_surface。它暴露了VkSurfaceKHR,它代表surface的一個抽象類型,用以呈現(xiàn)渲染圖像使用。我們程序中將要使用到的surface是由我們已經(jīng)引入的GLFW擴展及其打開的相關(guān)窗體支持的。簡單來說surface就是Vulkan與窗體系統(tǒng)的連接橋梁。

 

VK_KHR_surface擴展是一個instance級擴展,我們目前為止已經(jīng)啟用過它,它包含在glfwGetRequiredInstanceExtensions返回的列表中。該列表還包括將在接下來幾小節(jié)中使用的一些其他WSI擴展。

 

需要在instance創(chuàng)建之后立即創(chuàng)建窗體surface,因為它會影響物理設(shè)備的選擇。之所以在本小節(jié)將surface創(chuàng)建邏輯納入討論范圍,是因為窗體surface對于渲染、呈現(xiàn)方式是一個比較大的課題,如果過早的在創(chuàng)建物理設(shè)備加入這部分內(nèi)容,會混淆基本的物理設(shè)備設(shè)置工作。另外窗體surface本身對于Vulkan也是非強制的。Vulkan允許這樣做,不需要同OpenGL一樣必須要創(chuàng)建窗體surface。

Window surface creation


現(xiàn)在開始著手創(chuàng)建窗體surface,在類成員debugCallback下加入成員變量surface。

VkSurfaceKHR surface;

雖然VkSurfaceKHR對象及其用法與平臺無關(guān)聯(lián),但創(chuàng)建過程需要依賴具體的窗體系統(tǒng)的細節(jié)。比如,在Windows平臺中,它需要WIndows上的HWNDHMODULE句柄。因此針對特定平臺提供相應的擴展,在Windows上為VK_KHR_win32_surface,它自動包含在glfwGetRequiredInstanceExtensions列表中。

 

我們將會演示如何使用特定平臺的擴展來創(chuàng)建Windows上的surface橋,但是不會在教程中實際使用它。使用GLFW這樣的庫避免了編寫沒有任何意義的跨平臺相關(guān)代碼。GLFW實際上通過glfwCreateWindowSurface很好的處理了平臺差異性。當然了,比較理想是在依賴它們幫助我們完成具體工作之前,了解一下背后的實現(xiàn)是有幫助的。

 

因為一個窗體surface是一個Vulkan對象,它需要填充VkWin32SurfaceCreateInfoKHR結(jié)構(gòu)體,這里有兩個比較重要的參數(shù):hwndhinstance。如果熟悉windows下開發(fā)應該知道,這些是窗口和進程的句柄。

VkWin32SurfaceCreateInfoKHR createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
createInfo.hwnd = glfwGetWin32Window(window);
createInfo.hinstance = GetModuleHandle(nullptr);

glfwGetWin32Window函數(shù)用于從GLFW窗體對象獲取原始的HWNDGetModuleHandle函數(shù)返回當前進程的HINSTANCE句柄。

 

填充完結(jié)構(gòu)體之后,可以利用vkCreateWin32SurfaceKHR創(chuàng)建surface橋,和之前獲取創(chuàng)建、銷毀DebugReportCallEXT一樣,這里同樣需要通過instance獲取創(chuàng)建surface用到的函數(shù)。這里涉及到的參數(shù)分別為instance, surface創(chuàng)建的信息,自定義分配器和最終保存surface的句柄變量。

auto CreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR) vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR");if (!CreateWin32SurfaceKHR || CreateWin32SurfaceKHR(instance, &createInfo, nullptr, &surface) != VK_SUCCESS) {    throw std::runtime_error("failed to create window surface!");
}

該過程與其他平臺類似,比如Linux,使用X11界面窗體系統(tǒng),可以通過vkCreateXcbSurfaceKHR函數(shù)建立連接。

 

glfwCreateWindowSurface函數(shù)根據(jù)不同平臺的差異性,在實現(xiàn)細節(jié)上會有所不同。我們現(xiàn)在將其整合到我們的程序中。從initVulkan中添加一個函數(shù)createSurface,安排在createInstnacesetupDebugCallback函數(shù)之后。

電腦培訓,計算機培訓,平面設(shè)計培訓,網(wǎng)頁設(shè)計培訓,美工培訓,Web培訓,Web前端開發(fā)培訓

void initVulkan() {
    createInstance();
    setupDebugCallback();
    createSurface();
    pickPhysicalDevice();
    createLogicalDevice();
}void createSurface() {

}

電腦培訓,計算機培訓,平面設(shè)計培訓,網(wǎng)頁設(shè)計培訓,美工培訓,Web培訓,Web前端開發(fā)培訓

GLFW沒有使用結(jié)構(gòu)體,而是選擇非常直接的參數(shù)傳遞來調(diào)用函數(shù)。

void createSurface() {    if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) {        throw std::runtime_error("failed to create window surface!");
    }
}

參數(shù)是VkInstance,GLFW窗體的指針,自定義分配器和用于存儲VkSurfaceKHR變量的指針。對于不同平臺統(tǒng)一返回VkResult。GLFW沒有提供專用的函數(shù)銷毀surface,但是可以簡單的通過Vulkan原始的API完成:

void cleanup() {
        ...
        vkDestroySurfaceKHR(instance, surface, nullptr);
        vkDestroyInstance(instance, nullptr);
        ...
    }

最后請確保surface的清理是在instance銷毀之前完成。

Querying for presentation support


雖然Vulkan的實現(xiàn)支持窗體集成功能,但是并不意味著系統(tǒng)中的每一個物理設(shè)備都支持它。因此,我們需要擴展isDeviceSuitable函數(shù),確保設(shè)備可以將圖像呈現(xiàn)到我們創(chuàng)建的surface。由于presentation是一個隊列的特性功能,因此解決問題的方法就是找到支持presentation的隊列簇,最終獲取隊列滿足surface創(chuàng)建的需要。

 

實際情況是,支持graphics命令的的隊列簇和支持presentation命令的隊列簇可能不是同一個簇。因此,我們需要修改QueueFamilyIndices結(jié)構(gòu)體,以支持差異化的存儲。

電腦培訓,計算機培訓,平面設(shè)計培訓,網(wǎng)頁設(shè)計培訓,美工培訓,Web培訓,Web前端開發(fā)培訓

struct QueueFamilyIndices {    int graphicsFamily = -1;    int presentFamily = -1;    bool isComplete() {        return graphicsFamily >= 0 && presentFamily >= 0;
    }
};

電腦培訓,計算機培訓,平面設(shè)計培訓,網(wǎng)頁設(shè)計培訓,美工培訓,Web培訓,Web前端開發(fā)培訓

接下來,我們修改findQueueFamilies函數(shù)來查找具備presentation功能的隊列簇。函數(shù)中用于檢查的核心代碼是vkGetPhysicalDeviceSurfaceSupportKHR,它將物理設(shè)備、隊列簇索引和surface作為參數(shù)。在VK_QUEUE_GRAPHICS_BIT相同的循環(huán)體中添加函數(shù)的調(diào)用:

VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);

然后之需要檢查布爾值并存儲presentation隊列簇的索引:

if (queueFamily.queueCount > 0 && presentSupport) {
    indices.presentFamily = i;
}

需要注意的是,很可能得到的是同一個隊列簇,但是在我們的程序結(jié)構(gòu)中,我們始終將他們視為統(tǒng)一的隊列簇,即使它們分別來自不同的隊列簇。然而,處于性能的考慮,我們可以通過添加邏輯明確的指定物理設(shè)備所使用的graphics和presentation功能來自同一個隊列簇。

Creating the presentation queue


 

剩下的事情是修改邏輯設(shè)備創(chuàng)建過程,在于創(chuàng)建presentation隊列并獲取VkQueue的句柄。添加保存隊列句柄的成員變量:

VkQueue presentQueue;

接下來,我們需要多個VkDeviceQueueCreateInfo結(jié)構(gòu)來創(chuàng)建不同功能的隊列。一個優(yōu)雅的方式是針對不同功能的隊列簇創(chuàng)建一個set集合確保隊列簇的唯一性:

電腦培訓,計算機培訓,平面設(shè)計培訓,網(wǎng)頁設(shè)計培訓,美工培訓,Web培訓,Web前端開發(fā)培訓

#include <set>...

QueueFamilyIndices indices = findQueueFamilies(physicalDevice);

std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
std::set<int> uniqueQueueFamilies = {indices.graphicsFamily, indices.presentFamily};float queuePriority = 1.0f;for (int queueFamily : uniqueQueueFamilies) {
    VkDeviceQueueCreateInfo queueCreateInfo = {};
    queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
    queueCreateInfo.queueFamilyIndex = queueFamily;
    queueCreateInfo.queueCount = 1;
    queueCreateInfo.pQueuePriorities = &queuePriority;
    queueCreateInfos.push_back(queueCreateInfo);
}

電腦培訓,計算機培訓,平面設(shè)計培訓,網(wǎng)頁設(shè)計培訓,美工培訓,Web培訓,Web前端開發(fā)培訓

同時還要修改VkDeviceCreateInfo指向隊列集合:

createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
createInfo.pQueueCreateInfos = queueCreateInfos.data();

如果隊列簇相同,那么我們之需要傳遞一次索引。最后,添加一個調(diào)用檢索隊列句柄:

vkGetDeviceQueue(device, indices.presentFamily, 0, &presentQueue);

在這個例子中,隊列簇是相同的,兩個句柄可能會有相同的值。在下一個章節(jié)中我們會看看交換鏈,以及它們?nèi)绾问刮覀兡軌驅(qū)D像呈現(xiàn)給surface。

http://www.cnblogs.com/heitao/p/6928590.html