バルカン。開発者ガイド。三角形を描く

私はIzhevskのCGTribeの翻訳者であり、VulkanAPIマニュアルの翻訳を引き続きアップロードしています。ソースリンク-vulkan-tutorial.com



この出版物は、三角形の描画セクション、つまりセットアップサブセクション、ベースコード、およびインスタンスの章の翻訳に専念しています。



コンテンツ
1.



2.



3.



4.





  1. (pipeline)


5.



  1. Staging


6. Uniform-



  1. layout
  2. sets


7.



  1. Image view image sampler
  2. image sampler


8.



9.



10. -



11. Multisampling



FAQ









ベースコード







一般的な構造



前の章では、Vulkanのプロジェクトを作成し、正しく構成し、コードスニペットを使用してテストする方法について説明しました。この章では、基本から始めます。



次のコードを検討してください。



#include <vulkan/vulkan.h>

#include <iostream>
#include <stdexcept>
#include <cstdlib>

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::exception& e) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}
      
      





まず、LunarGSDKのVulkanヘッダーファイルを含めます。ヘッダーファイルでstdexcepts



ありiostream



、エラー処理と配布に使用されます。ヘッダーファイルcstdlib



はマクロEXIT_SUCCESS



とを提供しますEXIT_FAILURE







プログラム自体はHelloTriangleApplicationクラスにラップされており、Vulkanオブジェクトをクラスのプライベートメンバーとして格納します。そこで、関数から呼び出される、各オブジェクトを初期化するための関数も追加しますinitVulkan



。その後、フレームをレンダリングするためのメインループを作成しましょう。これを行うにmainLoop



は、ウィンドウが閉じるまでループが実行される関数を入力します。ウィンドウを閉じて終了した後、mainLoop



リソースを解放する必要があります。これを行うには、を入力しcleanup



ます。



操作中に重大なエラーが発生した場合、std::runtime_error



関数main



キャッチされる例外がスローされ、説明がに表示されstd::cerr



ます。このようなエラーの1つは、たとえば、必要な拡張子がサポートされていないというメッセージである可能性があります。標準の例外タイプの多くを処理するために、より一般的なものをキャッチしますstd::exception







以降のほぼすべての章では、から呼び出される新しい関数と、プログラムの最後にinitVulkan



リリースする必要のある新しいVulkanオブジェクトが追加されcleanup



ます。



資源管理



Vulkanオブジェクトが不要になった場合は、破棄する必要があります。 C ++を使用すると、RAIIまたはヘッダーファイルによって提供されるスマートポインターを使用して、リソースの割り当てを自動的に解除できます<memory>



。ただし、このチュートリアルでは、Vulkanオブジェクトをいつ割り当ておよび割り当て解除するかを明示的に記述することにしました。結局のところ、これはVulkanの仕事の特徴であり、起こりうる間違いを避けるために各操作を詳細に説明することです。



チュートリアルを読んだ後、コンストラクタでVulkanオブジェクトを受け取り、デストラクタでそれらの割り当てを解除するC ++クラスを作成することにより、自動リソース管理を実装できます。要件に応じて、std::unique_ptr



またはの独自の削除機能を実装することもできstd::shared_ptr



ます。 RAIIの概念は、大規模なプログラムに推奨されますが、それについてさらに学ぶことは役に立ちます。



バルカンオブジェクトを直接ような関数を使用して作成されvkCreateXXXなど機能使用して、別のオブジェクトを介して割り当てられvkAllocateXXXをオブジェクトが他の場所で使用されていないことを確認した後、vkDestroyXXXまたはvkFreeXXXを使用してオブジェクトを破棄する必要がありますこれらの機能のパラメータは通常、オブジェクトのタイプによって異なりますが、共通のパラメータが1つありますpAllocator



これは、カスタムメモリ割り当てにコールバックを使用できるようにするオプションのパラメータです。マニュアルでは必要ありませんnullptr



引数として渡します



GLFW統合



Vulkanは、オフスクリーンレンダリングを使用する場合、ウィンドウを作成しなくても正常に機能しますが、結果が画面に表示される場合ははるかに優れています。

まず、行を#include <vulkan/vulkan.h>



次のように置き換えます。



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





関数initWindow



を追加し、run



他の呼び出しの前にメソッドからの呼び出しを追加します。initWindow



GLFWを使用して、ウィンドウを初期化および作成します。



void run() {
    initWindow();
    initVulkan();
    mainLoop();
    cleanup();
}

private:
    void initWindow() {

    }
      
      





の最初の呼び出しは、GLFWライブラリを初期化initWindow



する関数でglfwInit()



ある必要がありますGLFWは、もともとOpenGLで動作するように設計されました。OpenGLコンテキストは必要ないため、次の呼び出しを使用して作成する必要がないことを示します。



glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
      
      





この状況を処理するには個別の考慮が必要なため、ウィンドウのサイズを変更する機能を一時的に無効にします。



glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
      
      





ウィンドウを作成することは残っています。これを行うには、プライベートメンバーGLFWwindow* window;



追加し、次のコマンドでウィンドウを初期化します。



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





最初の3つのパラメーターは、ウィンドウの幅、高さ、およびタイトルを定義します。4番目のパラメーターはオプションで、ウィンドウが表示されるモニターを指定できます。最後のパラメーターはOpenGLに固有です。



これらの値は他の場所で必要になるため、ウィンドウの幅と高さに定数を使用すると便利です。クラス定義の前に次の行を追加しますHelloTriangleApplication







const uint32_t WIDTH = 800;
const uint32_t 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();
    }
}
      
      





このコードは問題を提起するべきではありません。ユーザーがウィンドウを閉じる前にXボタンを押すなどのイベントを処理します。また、このループから、個々のフレームをレンダリングする関数を呼び出します。



ウィンドウを閉じた後、リソースを解放してGLFWを終了する必要があります。まず、cleanup



次のコードに追加しましょう



void cleanup() {
    glfwDestroyWindow(window);

    glfwTerminate();
}
      
      





その結果、プログラムを開始した後Vulkan



、プログラムが閉じるまで表示される名前のウィンドウが表示されます。Vulkanを操作するためのスケルトンができたので、最初のVulkanオブジェクトの作成に移りましょう。



C ++コード











インスタンス







インスタンス化



最初に行う必要があるのは、ライブラリを初期化するためのインスタンスを作成することです。インスタンスは、プログラムとVulkanライブラリ間のリンクであり、インスタンスを作成するには、プログラムに関する情報をドライバーに提供する必要があります。



メソッドcreateInstance



追加し、関数から呼び出しますinitVulkan







void initVulkan() {
    createInstance();
}
      
      





インスタンスハンドルを保持するインスタンスメンバーをクラスに追加します。



private:
VkInstance instance;
      
      





次に、プログラムに関する情報を特別な構造に入力する必要があります。技術的には、データはオプションですが、これにより、ドライバーはプログラムでの作業を最適化するために役立つ情報を取得できます。この構造は呼ばれVkApplicationInfo



ます:



void createInstance() {
    VkApplicationInfo appInfo{};
    appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
    appInfo.pApplicationName = "Hello Triangle";
    appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.pEngineName = "No Engine";
    appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
    appInfo.apiVersion = VK_API_VERSION_1_0;
}
      
      





前述のように、Vulkanの多くの構造では、sTypeメンバーに明示的な型定義が必要です。また、この構造には、他の多くの構造と同様にpNext



、拡張機能の情報を提供できる要素含まれています。値の初期化を使用して、構造をゼロで埋めます。



Vulkanの情報のほとんどは構造を介して渡されるため、インスタンスを作成するのに十分な情報を提供するには、もう1つの構造を入力する必要があります。次の構造が必要です。これは、使用するグローバル拡張機能と検証レイヤーをドライバーに指示します。 「グローバル」とは、拡張機能が特定のデバイスではなく、プログラム全体に適用されることを意味します。



VkInstanceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
      
      





最初の2つのパラメーターは問題を引き起こしません。次の2つのメンバーは、必要なグローバル拡張を定義します。ご存知のように、VulkanAPIは完全にプラットフォームに依存しません。これは、ウィンドウシステムと対話するための拡張機能が必要であることを意味します。GLFWには、必要な拡張機能のリストを返す便利な組み込み関数があります。



uint32_t glfwExtensionCount = 0;
const char** glfwExtensions;

glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);

createInfo.enabledExtensionCount = glfwExtensionCount;
createInfo.ppEnabledExtensionNames = glfwExtensions;
      
      





最後の2つの構造メンバーは、含めるグローバル検証レイヤーを定義します。これらについては次の章で詳しく説明するので、今のところこれらの値は空白のままにしておきます。



createInfo.enabledLayerCount = 0;
      
      





これで、インスタンスの作成に必要なすべての作業が完了しました。電話をかけるvkCreateInstance







VkResult result = vkCreateInstance(&createInfo, nullptr, &instance);
      
      





原則として、オブジェクトを作成するための関数のパラメーターは次の順序です。



  • 必要な情報を含む構造へのポインタ
  • カスタムアロケータへのポインタ
  • 新しいオブジェクトの記述子が書き込まれる変数へのポインター


すべてが正しく行われると、インスタンス記述子はインスタンスに格納されますほとんどすべてのVulkan関数はVkResult値を返します。これは、VK_SUCCESS



エラーコードまたはエラーコードのいずれかです。インスタンスが作成されたことを確認するために結果を保存する必要はありません。簡単なチェックを使用しましょう:



if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
    throw std::runtime_error("failed to create instance!");
}
      
      





次に、プログラムを実行して、インスタンスが正常に作成されたことを確認します。



サポートされている拡張機能の確認



Vulkanの ドキュメントを見ると、考えられるエラーコードの1つがであることがわかりますVK_ERROR_EXTENSION_NOT_PRESENT



。必要な拡張機能を指定するだけで、サポートされていない場合は機能を停止できます。これは、ウィンドウシステムインターフェイスなどの主要な拡張機能には意味がありますが、オプション機能をテストしたい場合はどうでしょうか。



インスタンス化する前にサポートされている拡張機能のリストを取得するには、vkEnumerateInstanceExtensionProperties関数を使用します..。関数の最初のパラメーターはオプションであり、特定の検証レイヤーで拡張機能をフィルター処理できるため、ここでは空白のままにします。この関数には、拡張子の数が書き込まれる変数へのポインターと、拡張子に関する情報が書き込まれるメモリー領域へのポインターも必要です。



拡張情報を格納するためのメモリを割り当てるには、最初に拡張の数を知る必要があります。拡張機能の数を要求するには、最後のパラメーターを空白のままにします。



uint32_t extensionCount = 0;
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
      
      





拡張情報を格納するための配列を割り当てます(忘れないでくださいinclude <vector>



):



std::vector<VkExtensionProperties> extensions(extensionCount);
      
      





拡張機能に関する情報をリクエストできるようになりました。



vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());
      
      





VkExtensionProperties構造には、拡張機能の名前とバージョンが含まれています。それらは単純なforループでリストできます(\t



ここにインデントタブがあります):



std::cout << "available extensions:\n";

for (const auto& extension : extensions) {
    std::cout << '\t' << extension.extensionName << '\n';
}
      
      





createInstance



Vulkanサポートの詳細については 、このコードを関数に追加できます。関数によって返されるすべての拡張子glfwGetRequiredInstanceExtensions



がサポートされている拡張子のリストに含まれているかどうかを確認する関数を作成してみることもできます。





クリーニング



プログラムを閉じる前に、 VkInstanceを破棄する必要があります。これはcleanup



VkDestroyInstance関数を使用して実行できます



void cleanup() {
    vkDestroyInstance(instance, nullptr);

    glfwDestroyWindow(window);

    glfwTerminate();
}
      
      





vkDestroyInstance 関数のパラメーターは自明です。前の章で説明したように、Vulkanの割り当て関数と割り当て解除関数は、使用しないカスタムアロケーターへのオプションのポインターを受け入れ、を渡しnullptr



ます。インスタンスを破棄する前に、他のすべてのVulkanリソースをクリーンアップする必要があります。



より複雑な手順に進む前に、デバッグを容易にするために検証レイヤーを設定する必要があります。



C ++コード



All Articles