やっていくVulkan入門

3-1. イメージの作成

これから行う描画処理はメモリの中の話とはいえ、絵を描くキャンバスにあたるものが必要です。

とりあえず幅と高さは決めてしまいましょう。

const uint32_t screenWidth = 640;
const uint32_t screenHeight = 480;

Vulkanにおいて画像は vk::Image というオブジェクトで表されます。これはvk::DevicecreateImage メソッドで作成できます。

vk::ImageCreateInfo imgCreateInfo;
imgCreateInfo.imageType = vk::ImageType::e2D;
imgCreateInfo.extent = vk::Extent3D(screenWidth, screenHeight, 1);
imgCreateInfo.mipLevels = 1;
imgCreateInfo.arrayLayers = 1;
imgCreateInfo.format = vk::Format::eR8G8B8A8Unorm;
imgCreateInfo.tiling = vk::ImageTiling::eLinear;
imgCreateInfo.initialLayout = vk::ImageLayout::eUndefined;
imgCreateInfo.usage = vk::ImageUsageFlagBits::eColorAttachment;
imgCreateInfo.sharingMode = vk::SharingMode::eExclusive;
imgCreateInfo.samples = vk::SampleCountFlagBits::e1;

vk::UniqueImage image = device->createImageUnique(imgCreateInfo);

vk::ImageCreateInfo とかいうばかでかい初期化用構造体がありますね。ここでは要点となるメンバの説明だけ書くにとどめて、詳細については必要に応じておいおい解説していくことにします。気になるという人は公式ドキュメントを見てみてください。

imageType には画像の次元を指定します。1次元から3次元まで指定できます。2次元以外の画像ってなんじゃらほいという感じですが、Rampテクスチャやvoxelなど、普通に使い道があります。ここでは普通の2次元の画像のため vk::ImageType::e2D を指定しています。

extent には画像のサイズを指定します。ここでは640×480を指定しています。最大3次元の画像を扱うためvk::Extent3Dによる指定ですが、今回は2次元であるため、第3引数の奥行には1を指定しています。640x480x1のマス目と思ってください。

format には画素のフォーマットの種類を指定します。どのように各ピクセルが色を保存しているかの情報です。いろいろなフォーマットが存在するのですが、今回はひとまずR8G8B8A8Unormという形式を指定しています。RGB+A(透明度)をこの順でそれぞれ8bitで保存する形式になります。Unormの部分については今は無視しましょう。

これで画像オブジェクトを作成することができました。しかしこの段階ではまだ画像にメモリが割り当てられていません。メモリを割り当ててやらないと画像データを記録することは出来ません。


この節ではイメージの作成をやりました。次節ではメモリの確保をやります。

この節のコード
#include <vulkan/vulkan.hpp>
#include <iostream>
#include <vector>

const uint32_t screenWidth = 640;
const uint32_t screenHeight = 480;

int main() {
    vk::InstanceCreateInfo createInfo;

    vk::UniqueInstance instance;
    instance = vk::createInstanceUnique(createInfo);

    std::vector<vk::PhysicalDevice> physicalDevices = instance->enumeratePhysicalDevices();

    vk::PhysicalDevice physicalDevice;
    bool existsSuitablePhysicalDevice = false;
    uint32_t graphicsQueueFamilyIndex;

    for (size_t i = 0; i < physicalDevices.size(); i++) {
        std::vector<vk::QueueFamilyProperties> queueProps = physicalDevices[i].getQueueFamilyProperties();
        bool existsGraphicsQueue = false;

        for (size_t j = 0; j < queueProps.size(); j++) {
            if (queueProps[j].queueFlags & vk::QueueFlagBits::eGraphics) {
                existsGraphicsQueue = true;
                graphicsQueueFamilyIndex = j;
                break;
            }
        }

        if (existsGraphicsQueue) {
            physicalDevice = physicalDevices[i];
            existsSuitablePhysicalDevice = true;
            break;
        }
    }

    if (!existsSuitablePhysicalDevice) {
        std::cerr << "使用可能な物理デバイスがありません。" << std::endl;
        return -1;
    }

    vk::DeviceCreateInfo devCreateInfo;

    vk::DeviceQueueCreateInfo queueCreateInfo[1];
    queueCreateInfo[0].queueFamilyIndex = graphicsQueueFamilyIndex;
    queueCreateInfo[0].queueCount = 1;

    float queuePriorities[1] = { 1.0 };

    queueCreateInfo[0].pQueuePriorities = queuePriorities;

    devCreateInfo.pQueueCreateInfos = queueCreateInfo;
    devCreateInfo.queueCreateInfoCount = 1;
    vk::UniqueDevice device = physicalDevice.createDeviceUnique(devCreateInfo);

    vk::Queue graphicsQueue = device->getQueue(graphicsQueueFamilyIndex, 0);

    vk::CommandPoolCreateInfo cmdPoolCreateInfo;
    cmdPoolCreateInfo.queueFamilyIndex = graphicsQueueFamilyIndex;
    vk::UniqueCommandPool cmdPool = device->createCommandPoolUnique(cmdPoolCreateInfo);

    vk::CommandBufferAllocateInfo cmdBufAllocInfo;
    cmdBufAllocInfo.commandPool = cmdPool.get();
    cmdBufAllocInfo.commandBufferCount = 1;
    cmdBufAllocInfo.level = vk::CommandBufferLevel::ePrimary;
    std::vector<vk::UniqueCommandBuffer> cmdBufs =
        device->allocateCommandBuffersUnique(cmdBufAllocInfo);

    vk::ImageCreateInfo imgCreateInfo;
    imgCreateInfo.imageType = vk::ImageType::e2D;
    imgCreateInfo.extent = vk::Extent3D(screenWidth, screenHeight, 1);
    imgCreateInfo.mipLevels = 1;
    imgCreateInfo.arrayLayers = 1;
    imgCreateInfo.format = vk::Format::eR8G8B8A8Unorm;
    imgCreateInfo.tiling = vk::ImageTiling::eLinear;
    imgCreateInfo.initialLayout = vk::ImageLayout::eColorAttachmentOptimal;
    imgCreateInfo.usage = vk::ImageUsageFlagBits::eColorAttachment;
    imgCreateInfo.sharingMode = vk::SharingMode::eExclusive;
    imgCreateInfo.samples = vk::SampleCountFlagBits::e1;

    vk::UniqueImage image = device->createImageUnique(imgCreateInfo);

    vk::CommandBufferBeginInfo cmdBeginInfo;
    cmdBufs[0]->begin(cmdBeginInfo);

    // コマンドを記録

    cmdBufs[0]->end();

    vk::CommandBuffer submitCmdBuf[1] = { cmdBufs[0].get() };
    vk::SubmitInfo submitInfo;
    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = submitCmdBuf;

    graphicsQueue.submit({ submitInfo }, nullptr);

    return 0;
}
cmake_minimum_required(VERSION 3.22)

project(vulkan-test)

set(CMAKE_CXX_STANDARD 17)

add_executable(app main.cpp)

find_package(Vulkan REQUIRED)
target_include_directories(app PRIVATE ${Vulkan_INCLUDE_DIRS})
target_link_libraries(app PRIVATE ${Vulkan_LIBRARIES})