1. 程式人生 > >Vulkan Tutorial 02 編寫Vulkan應用程序框架原型

Vulkan Tutorial 02 編寫Vulkan應用程序框架原型

wikipedia doc lfw 高度 api tegra targe open width

操作系統:Windows8.1

顯卡:Nivida GTX965M

開發工具:Visual Studio 2017


General structure

在上一節中,我們創建了一個正確配置、可運行的的Vulkan應用程序,並使用測試代碼進行了測試。本節中我們從頭開始,使用如下代碼構建一個基於GLFW的Vulkan應用程序原型框架的雛形。

技術分享
#include <vulkan/vulkan.h>

#include <iostream>
#include <stdexcept>
#include <functional>

class HelloTriangleApplication {
public:
    void run() {
        initVulkan();
        mainLoop();
        cleanup();
    }

private:
    void initVulkan() {

    }

    void mainLoop() {

    }

    void cleanup() {

    }
};

int main() {
    HelloTriangleApplication app;

    try {
        app.run();
    } catch (const std::runtime_error& e) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}
技術分享

首先從LunarG SDK中添加Vulkan頭文件,它提供了購機愛你Vulkan應用程序需要的函數、結構體、和枚舉。我們包含stdexceptiostream頭文件用於拋出異常信息,而functional頭文件用於資源管理部分支持lambda表達式。

程序被封裝到一個類中,該類結構將會存儲Vulkan私有成員對象,並添加基本的函數來初始化他們。首先會從initVulkan函數開始調用。當一切準備好,我們進入主循環開始渲染幀。我們將會加入mainLoop函數包含loop循環調用,該循環調用直到GLFW窗體管理才會停止。當窗體關閉並且mainLoop返回時,我們需要釋放我們已經申請過的任何資源,該清理邏輯在cleanup

函數中去定義。

程序運行期間,如果發生了任何嚴重的錯誤異常,我們會拋出std::runtime_error 並註明異常描述信息,這個異常信息會被main函數捕獲及打印提示。很快你將會遇到一個拋出error的例子,是關於Vulkan應用程序不支持某個必要的擴展功能。

基本上在之後的每一個小節中都會從initVulkan函數中增加一個新的Vulkan函數調用,增加的函數會產生Vulkan objects 並保存為類的私有成員,請記得在cleanup中進行資源的清理和釋放。

Resource management


我們知道通過malloc分配的每一個內存快在使用完之後都需要free內存資源,每一個我們創建的Vulkan object不在使用時都需要明確的銷毀。在C++中可以利用<memory>

完成 auto 資源管理,但是在本節中,選擇明確編寫所有的內存的分配和釋放操作,其主要原因是Vulkan的設計理念就是明確每一步操作,清楚每一個對象的生命周期,避免可能存在的未知代碼造成的異常。

當然在本節之後,我們可以通過重載std::shared_ptr來實現auto 資源管理。對於更大體量的Vulkan程序,建議遵循RAII的原則維護資源的管理。

Vulkan對象可以直接使用vkCreateXXX系函數創建,也可以通過具有vkAllocateXXX等功能的一個對象進行分配。確保每一個對象在不使用的時候調用vkDestroyXXXvkFreeXXX銷毀、釋放對應的資源。這些函數的參數通常因不同類型的對象而不同,但是他們共享一個參數:pAllocator。這是一個可選的參數,Vulkan允許我們自定義內存分配器。我們將在本教程忽略此參數,始終以nullptr作為參數。

Integrating GLFW


如果我們開發一些不需要基於屏幕顯示的程序,那麽純粹的Vulkan本身可以完美的支持開發。但是如果創建一些讓人興奮的可視化的內容,我們就需要引入窗體系統GLFW,並將#include <vulkan/vulkan.h> 進行相應的替換。

#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>

在新版本的GLFW中已經提供了Vulkan相關的支持,詳細的使用建議參閱官方資料。

技術分享

通過替換,將會使用GLFW對Vulkan的支持,並自動加載Vulkan的頭文件。在run函數中添加一個initWindow函數調用,並確保在其他函數調用前優先調用。我們將會通過該函數完成GLFW的窗體初始化工作。

技術分享
void run() {
    initWindow();
    initVulkan();
    mainLoop();
    cleanup();
}

private:
    void initWindow() {

    }
技術分享

initWindow中的第一個調用是glfwInit(),它會初始化GLFW庫。因為最初GLFW是為OpenGL創建上下文,所以在這裏我們需要告訴它不要調用OpenGL相關的初始化操作。

glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);

特別註意窗口大小的設置,稍後我們會調用,現在使用另一個窗口提示來僅用它。

glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);

現在剩下的就是創建實際的窗體。添加一個GLFWwindow*窗體,私有類成員存儲其引用並初始化窗體:

window = glfwCreateWindow(800, 600, "Vulkan", nullptr, nullptr);

前三個參數定義窗體的寬度、高度和Title。第四個參數允許制定一個監聽器來打開窗體,最後一個參數與OpenGL有關,我們選擇nullptr。

使用常量代替硬編碼寬度和高度,因為我們在後續的內容中會引用該數值多次。在HelloTriangleApplication類定義之上添加以下幾行:

const int WIDTH = 800;
const int HEIGHT = 600;

並替換窗體創建的代碼語句為:

window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);

你現在應該有一個如下所示的initWindow函數:

技術分享
void initWindow() {
    glfwInit();

    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
    glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);

    window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);
}
技術分享

保持程序運行,直到發生錯誤或者窗體關閉,我們需要向mainLoop函數添加事件循環,如下所示:

void mainLoop() {
    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();
    }
}

這段代碼應該很容易看懂。它循環並檢查GLFW事件,直到按下X按鈕,或者關閉窗體。該循環結構稍後會調用渲染函數。

一旦窗體關閉,我們需要通過cleanup函數清理資源、結束GLFW本身。

void cleanup() {
    glfwDestroyWindow(window);

    glfwTerminate();
}

運行程序,我們應該會看到一個名為Vulkan的白色窗體,直到關閉窗體終止應用程序。

技術分享

ok,到現在我們已經完成了一個Vulkan程序的骨架原型,在下一小節我們會創建第一個Vulkan Object!

獲取工程代碼 Github checkout

Vulkan Tutorial 02 編寫Vulkan應用程序框架原型