3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Vulkan Tutorial (Drawing a triangle/Setup/Base code) 日本語訳

Last updated at Posted at 2022-06-26

基本となるコード (Base code)

全体的な構造 (General structure)

前章では、適切な設定を全て行った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;
}

最初に、LunarG SDK の Vulkan ヘッダをインクルードし、関数、構造体、enumを使えるようにします。stdexceptiostreamヘッダはエラーの伝搬とレポートのためにインクルードされます。cstdlibヘッダはEXIT_SUCCESSEXIT_FAILUREマクロを提供します。

プログラム自体はクラスにラッピングされ、Vulkan オブジェクトはそのクラスのプライベートメンバ変数として格納されます。それらの変数を初期化する関数を追加し、その関数はinitVulkan関数から呼ばれます。全ての準備ができたら、フレームのレンダリングを始めるためにメインループに入ります。この後すぐ、ウィンドウが閉じられるまで繰り返すループをmainLoop関数に追加します。ウィンドウが閉じられてmainLoopから抜けたら、cleanup関数でリソースを開放するようにします。

もし実行中に何らかの致命的なエラーが発生したら、説明的なメッセージとともにstd::runtime_error例外が投げられ、それはmain関数まで伝搬してコマンドプロンプトに表示されます。標準的な例外を継承した様々なクラスを扱えるようにするため、より一般的なstd::exceptionをキャッチします。一つの例として、要求された拡張がサポートされていなかったというものがあり、これはすぐに対処する予定です。

これ以降の章ではだいたい、initVulkanから呼び出される関数が1つ追加され、1つ以上の新しいVulkanオブジェクトがプライベートメンバとして追加され、それをcleanup関数で解放する必要があります。

リソース管理 (Resource management)

mallocで確保したメモリチャンクは必ずfreeを呼び出して解放しなければいけないように、作成された全てのVulkanオブジェクトは、必要なくなれば明示的に破棄される必要があります。C++では、RAII<memory>ヘッダで提供されるスマートポインタを使って、リソース管理を自動で行うことができます。しかし、このチュートリアルではVulkanオブジェクトの確保と解放を明示的に行うようにしました。何と言っても、Vulkanの特徴は間違いを避けるために全ての命令を明示することなので、オブジェクトのライフタイムを明示的に扱うのはAPIの仕組みを学ぶのに良いことです。

このチュートリアルが終わったあと、あなたはコンストラクタでVulkanオブジェクトを構築してデストラクタで解放するようなクラスを書くか、要求する所有権によってstd::unique_ptrstd::shared_ptrのいずれかにカスタムデリータを渡すことで、自動的なリソース管理を実装することができます。RAIIは大規模なVulkanプログラムでは推奨されるモデルですが、学習目的では舞台裏で何が行われているかを知るのは良いことです。

VulkanオブジェクトはvkCreateXXXのような関数で直接作成されるか、または別のオブジェクトを通じてvkAllocateXXXのような関数で割り当てられます。オブジェクトがもうどこからも使われないのを確認したら、対となる関数であるvkDestroyXXXvkFreeXXXで破棄する必要があります。これらの関数に渡すパラメータは、一般的にはオブジェクトの型によって違いますが、一つのパラメータ、pAllocatorだけは共通です。これはカスタムメモリアロケータのコールバックを指定する追加のパラメータです。私達はこのチュートリアルではこのパラメータを無視して、常にnullptrを渡します。

GLFWの統合 (Integrating GLFW)

Vulkanは、オフスクリーンレンダリングを使いたいのであれば、ウィンドウを作成しなくても完全に動作しますが、実際に何かを表示するほうがエキサイティングでしょう!最初に#include <vulkan/vulkan.h>の行を次のように置き換えます:

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

こうすることで、GLFWは自身の定義を含み、Vulkanヘッダを自動的に一緒にロードします。initWindow関数を追加してrun関数で最初に呼び出されるようにしてください。この関数を使ってGLFWの初期化とウィンドウの作成を行います。

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

private:
    void initWindow() {

    }

initWindowで最初に、GLFWライブラリを初期化する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++ code

前の記事
次の記事

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?