やっていくVulkan入門

6-5. シェーダからテクスチャへのアクセス

この節ではいよいよシェーダからテクスチャを利用します。


前回まででシェーダにテクスチャデータを渡すコードを書き終えてたので、あとはシェーダを書くだけです。

といいつつ、もうちょっとだけシェーダに渡す値があります。それは何かというと、テクスチャ座標です。

テクスチャ座標情報の受け渡し

テクスチャを3Dモデルに貼り付けるならば、「テクスチャ画像上のどの位置をどの頂点に対応させるのか」という情報を指定する必要があります。 ということで、各頂点にテクスチャ座標の情報を持たせなければいけません。

シェーダに渡す頂点データの内容を追加しましょう。やり方が思い出せない人は5章6節を参照してください。

struct Vertex {
    Vec2 pos;
    Vec3 color;
    Vec2 texUV; // テクスチャ座標
};

std::vector<Vertex> vertices = {
    Vertex{Vec2{-0.5f, -0.5f}, Vec3{0.0, 0.0, 1.0}, Vec2{0.0, 0.0}},
    Vertex{Vec2{0.5f, 0.5f}, Vec3{0.0, 1.0, 0.0}, Vec2{1.0, 1.0}},
    Vertex{Vec2{-0.5f, 0.5f}, Vec3{1.0, 0.0, 0.0}, Vec2{0.0, 1.0}},
    Vertex{Vec2{0.5f, -0.5f}, Vec3{1.0, 1.0, 1.0}, Vec2{1.0, 0.0}},
};

頂点入力デスクリプションも忘れずに対応させましょう。

vk::VertexInputAttributeDescription vertexInputDescription[3];
vertexInputDescription[0].binding = 0;
vertexInputDescription[0].location = 0;
vertexInputDescription[0].format = vk::Format::eR32G32Sfloat;
vertexInputDescription[0].offset = offsetof(Vertex, pos);
vertexInputDescription[1].binding = 0;
vertexInputDescription[1].location = 1;
vertexInputDescription[1].format = vk::Format::eR32G32B32Sfloat;
vertexInputDescription[1].offset = offsetof(Vertex, color);
// 追加
vertexInputDescription[2].binding = 0;
vertexInputDescription[2].location = 2;
vertexInputDescription[2].format = vk::Format::eR32G32Sfloat;
vertexInputDescription[2].offset = offsetof(Vertex, texUV);

vk::PipelineVertexInputStateCreateInfo vertexInputInfo;
vertexInputInfo.vertexBindingDescriptionCount = std::size(vertexBindingDescription); // == 1
vertexInputInfo.pVertexBindingDescriptions = vertexBindingDescription;
vertexInputInfo.vertexAttributeDescriptionCount = std::size(vertexInputDescription); // == 3
vertexInputInfo.pVertexAttributeDescriptions = vertexInputDescription;

座標値は0.01.0で指定します。 画像データは何十ピクセルもあるのにです。これはどういうことかと言うと、 正規化座標で表すためです。

正規化テクスチャ座標

正規化座標というのは、画像のピクセル数に関わらず一番左上を(0.0, 0.0)、一番右下を(1.0, 1.0)とする座標系です。

不自然に見えるかもしれませんが、これによって具体的な画像の解像度に依らない処理になるという利点もあります。3DCGにおいては基本的にこちらの方が標準です。

サンプラの設定次第で正規化せずに取り扱うこともできます。

シェーダを書く

いよいよテクスチャを利用するためのシェーダを書きます。

まずは頂点シェーダから。

#version 450
#extension GL_ARB_separate_shader_objects : enable

layout(set = 0, binding = 0) uniform SceneData {
    vec2 rectCenter;
} sceneData;

layout(location = 0) in vec2 inPos;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexUV;   // テクスチャ座標入力
layout(location = 0) out vec3 fragmentColor;
layout(location = 1) out vec2 fragmentTexUV;    // フラグメントシェーダへのテクスチャ座標出力

void main() {
    gl_Position = vec4(sceneData.rectCenter + inPos, 0.0, 1.0);
    fragmentColor = inColor;
    fragmentTexUV = inTexUV;    // テクスチャ座標
}

頂点シェーダではテクスチャに直接は触りませんが、必要のためフラグメントシェーダへテクスチャ座標の情報を受け渡ししています。

次は重要なフラグメントシェーダです。

#version 450
#extension GL_ARB_separate_shader_objects : enable

layout(set = 0, binding = 1) uniform sampler2D texSampler; // テクスチャサンプラ

layout(location = 0) in vec3 fragmentColor;
layout(location = 1) in vec2 fragmentTexUV;
layout(location = 0) out vec4 outColor;

void main() {
    outColor = texture(texSampler, fragmentTexUV);  // テクスチャサンプリング
}

まずは4行目に注目しましょう。デスクリプタを用いて渡したテクスチャサンプラは、sampler2Dという型の値として受け取ります。復習となりますが、bindingの値がdescSetLayoutBinding[1].bindingに指定した値と一致させてあることに注意してください。

次にmain関数の中身に注目しましょう。出力する色の値outColorを決めるために、textureという見慣れない関数が使われています。 これはテクスチャを扱うためのGLSL組み込みの関数です。

第1引数にテクスチャサンプラを指定します。 ここではtexSamplerを指定しています。

第2引数にサンプリングするテクスチャ座標を指定します。 ここでは頂点シェーダから受け取ったfragmentTexUVを指定しています。

それでは実行してみましょう。シェーダをコンパイルし直すことも忘れずに。

画面に画像が表示されたのではないでしょうか。

image.jpgを別の画像ファイルに変えて実験してみても良いでしょう。画像の解像度や縦横比に関わらず、同じ四角形に伸ばされたり押し縮められて表示されるはずです。正規化座標の作用です。

仕組み

画像が表示されたのはいいものの、今一つ仕組みが分からないという方もいるのではないでしょうか。各ピクセルのフラグメントシェーダでテクスチャサンプリングとやらをして、それを集めるとどうして画像になるのでしょうか。

復習をしましょう。5章7節において、頂点シェーダからフラグメントシェーダに値を渡すとき、その値は線形補間されるということを学びました。

これを踏まえると、フラグメントシェーダに渡されたtexCoordの値はどうなっているでしょうか?もちろん各頂点のtexCoordの値の線形補間となっています。 その結果、各ピクセル(フラグメント)において、渡されるtexCoordの値は以下のような雰囲気で決まっていきます。

これらの座標を各フラグメントシェーダがサンプリングした結果、「テクスチャが貼り付けられた」かのような表現ができるのです。これで謎は解決しました。

テクスチャのほかの利用法

重要なことは、このように「ただモデルに貼り付ける」というテクスチャの利用法は数ある利用法の内の1つでしかないということです。

今回のコードでは、頂点シェーダから受け取った座標値をそのままテクスチャ座標として利用しました。テクスチャから読みだした値をそのまま色の値として利用しました。しかしこれらは所詮「このシェーダのコードではそうしている」ということに過ぎません。各フラグメントシェーダで変なテクスチャ座標値を計算してアクセスしても良いし、読みだした値を直接的に色に利用するのではなく陰の付き方であるとか表面の質感を表す情報であるとか、より高度な別の用途に使っても良いのです。


この節ではシェーダからテクスチャサンプリングを行いました。

次節では応用的なテクスチャの利用法をいくつか紹介します。

この節のコード
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include <vulkan/vulkan.hpp>
#include <GLFW/glfw3.h>
#include <filesystem>
#include <fstream>
#include <iostream>

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

struct Vec2 {
    float x, y;
};

struct Vec3 {
    float x, y, z;
};

struct Vertex {
    Vec2 pos;
    Vec3 color;
    Vec2 texUV;
};

std::vector<Vertex> vertices = {
    Vertex{Vec2{-0.5f, -0.5f}, Vec3{0.0, 0.0, 1.0}, Vec2{0.0, 0.0}},
    Vertex{Vec2{0.5f, 0.5f}, Vec3{0.0, 1.0, 0.0}, Vec2{1.0, 1.0}},
    Vertex{Vec2{-0.5f, 0.5f}, Vec3{1.0, 0.0, 0.0}, Vec2{0.0, 1.0}},
    Vertex{Vec2{0.5f, -0.5f}, Vec3{1.0, 1.0, 1.0}, Vec2{1.0, 0.0}},
};

std::vector<uint16_t> indices = {0, 1, 2, 1, 0, 3};

struct SceneData {
    Vec2 rectCenter;
};

SceneData sceneData = { Vec2{ 0.3, -0.2 } };

int main() {
    if (!glfwInit())
        return -1;

    uint32_t requiredExtensionsCount;
    const char **requiredExtensions = glfwGetRequiredInstanceExtensions(&requiredExtensionsCount);

    vk::InstanceCreateInfo createInfo;
    createInfo.enabledExtensionCount = requiredExtensionsCount;
    createInfo.ppEnabledExtensionNames = requiredExtensions;

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

    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
    GLFWwindow *window;
    window = glfwCreateWindow(screenWidth, screenHeight, "GLFW Test Window", NULL, NULL);
    if (!window) {
        const char *err;
        glfwGetError(&err);
        std::cout << err << std::endl;
        glfwTerminate();
        return -1;
    }

    VkSurfaceKHR c_surface;
    auto result = glfwCreateWindowSurface(instance.get(), window, nullptr, &c_surface);
    if (result != VK_SUCCESS) {
        const char *err;
        glfwGetError(&err);
        std::cout << err << std::endl;
        glfwTerminate();
        return -1;
    }
    vk::UniqueSurfaceKHR surface{c_surface, instance.get()};

    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 && physicalDevices[i].getSurfaceSupportKHR(j, surface.get())) {
                existsGraphicsQueue = true;
                graphicsQueueFamilyIndex = j;
                break;
            }
        }

        std::vector<vk::ExtensionProperties> extProps = physicalDevices[i].enumerateDeviceExtensionProperties();
        bool supportsSwapchainExtension = false;

        for (size_t j = 0; j < extProps.size(); j++) {
            if (std::string_view(extProps[j].extensionName.data()) == VK_KHR_SWAPCHAIN_EXTENSION_NAME) {
                supportsSwapchainExtension = true;
                break;
            }
        }

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

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

    vk::DeviceCreateInfo devCreateInfo;

    auto devRequiredExtensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};

    devCreateInfo.enabledExtensionCount = devRequiredExtensions.size();
    devCreateInfo.ppEnabledExtensionNames = devRequiredExtensions.begin();

    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::PhysicalDeviceMemoryProperties memProps = physicalDevice.getMemoryProperties();

    vk::BufferCreateInfo vertBufferCreateInfo;
    vertBufferCreateInfo.size = sizeof(Vertex) * vertices.size();
    vertBufferCreateInfo.usage = vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eTransferDst;
    vertBufferCreateInfo.sharingMode = vk::SharingMode::eExclusive;

    vk::UniqueBuffer vertexBuf = device->createBufferUnique(vertBufferCreateInfo);

    vk::MemoryRequirements vertexBufMemReq = device->getBufferMemoryRequirements(vertexBuf.get());

    vk::MemoryAllocateInfo vertexBufMemAllocInfo;
    vertexBufMemAllocInfo.allocationSize = vertexBufMemReq.size;

    bool suitableMemoryTypeFound = false;
    for (uint32_t i = 0; i < memProps.memoryTypeCount; i++) {
        if (vertexBufMemReq.memoryTypeBits & (1 << i) && (memProps.memoryTypes[i].propertyFlags & vk::MemoryPropertyFlagBits::eDeviceLocal)) {
            vertexBufMemAllocInfo.memoryTypeIndex = i;
            suitableMemoryTypeFound = true;
            break;
        }
    }
    if (!suitableMemoryTypeFound) {
        std::cerr << "適切なメモリタイプが存在しません。" << std::endl;
        return -1;
    }

    vk::UniqueDeviceMemory vertexBufMemory = device->allocateMemoryUnique(vertexBufMemAllocInfo);

    device->bindBufferMemory(vertexBuf.get(), vertexBufMemory.get(), 0);

    {
        vk::BufferCreateInfo stagingBufferCreateInfo;
        stagingBufferCreateInfo.size = sizeof(Vertex) * vertices.size();
        stagingBufferCreateInfo.usage = vk::BufferUsageFlagBits::eTransferSrc;
        stagingBufferCreateInfo.sharingMode = vk::SharingMode::eExclusive;

        vk::UniqueBuffer stagingBuf = device->createBufferUnique(stagingBufferCreateInfo);

        vk::MemoryRequirements stagingBufMemReq = device->getBufferMemoryRequirements(stagingBuf.get());

        vk::MemoryAllocateInfo stagingBufMemAllocInfo;
        stagingBufMemAllocInfo.allocationSize = stagingBufMemReq.size;

        suitableMemoryTypeFound = false;
        for (uint32_t i = 0; i < memProps.memoryTypeCount; i++) {
            if (stagingBufMemReq.memoryTypeBits & (1 << i) && (memProps.memoryTypes[i].propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible)) {
                stagingBufMemAllocInfo.memoryTypeIndex = i;
                suitableMemoryTypeFound = true;
                break;
            }
        }
        if (!suitableMemoryTypeFound) {
            std::cerr << "適切なメモリタイプが存在しません。" << std::endl;
            return -1;
        }

        vk::UniqueDeviceMemory stagingBufMemory = device->allocateMemoryUnique(stagingBufMemAllocInfo);

        device->bindBufferMemory(stagingBuf.get(), stagingBufMemory.get(), 0);

        void *pStagingBufMem = device->mapMemory(stagingBufMemory.get(), 0, sizeof(Vertex) * vertices.size());

        std::memcpy(pStagingBufMem, vertices.data(), sizeof(Vertex) * vertices.size());

        vk::MappedMemoryRange flushMemoryRange;
        flushMemoryRange.memory = stagingBufMemory.get();
        flushMemoryRange.offset = 0;
        flushMemoryRange.size = sizeof(Vertex) * vertices.size();

        device->flushMappedMemoryRanges({flushMemoryRange});

        device->unmapMemory(stagingBufMemory.get());

        vk::CommandPoolCreateInfo tmpCmdPoolCreateInfo;
        tmpCmdPoolCreateInfo.queueFamilyIndex = graphicsQueueFamilyIndex;
        tmpCmdPoolCreateInfo.flags = vk::CommandPoolCreateFlagBits::eTransient;
        vk::UniqueCommandPool tmpCmdPool = device->createCommandPoolUnique(tmpCmdPoolCreateInfo);

        vk::CommandBufferAllocateInfo tmpCmdBufAllocInfo;
        tmpCmdBufAllocInfo.commandPool = tmpCmdPool.get();
        tmpCmdBufAllocInfo.commandBufferCount = 1;
        tmpCmdBufAllocInfo.level = vk::CommandBufferLevel::ePrimary;
        std::vector<vk::UniqueCommandBuffer> tmpCmdBufs = device->allocateCommandBuffersUnique(tmpCmdBufAllocInfo);

        vk::BufferCopy bufCopy;
        bufCopy.srcOffset = 0;
        bufCopy.dstOffset = 0;
        bufCopy.size = sizeof(Vertex) * vertices.size();

        vk::CommandBufferBeginInfo cmdBeginInfo;
        cmdBeginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;

        tmpCmdBufs[0]->begin(cmdBeginInfo);
        tmpCmdBufs[0]->copyBuffer(stagingBuf.get(), vertexBuf.get(), {bufCopy});
        tmpCmdBufs[0]->end();

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

        graphicsQueue.submit({submitInfo});
        graphicsQueue.waitIdle();
    }

    vk::BufferCreateInfo indexBufferCreateInfo;
    indexBufferCreateInfo.size = sizeof(uint16_t) * indices.size();
    indexBufferCreateInfo.usage = vk::BufferUsageFlagBits::eIndexBuffer | vk::BufferUsageFlagBits::eTransferDst;
    indexBufferCreateInfo.sharingMode = vk::SharingMode::eExclusive;

    vk::UniqueBuffer indexBuf = device->createBufferUnique(indexBufferCreateInfo);

    vk::MemoryRequirements indexBufMemReq = device->getBufferMemoryRequirements(indexBuf.get());

    vk::MemoryAllocateInfo indexBufMemAllocInfo;
    indexBufMemAllocInfo.allocationSize = indexBufMemReq.size;

    suitableMemoryTypeFound = false;
    for (uint32_t i = 0; i < memProps.memoryTypeCount; i++) {
        if (indexBufMemReq.memoryTypeBits & (1 << i) && (memProps.memoryTypes[i].propertyFlags & vk::MemoryPropertyFlagBits::eDeviceLocal)) {
            indexBufMemAllocInfo.memoryTypeIndex = i;
            suitableMemoryTypeFound = true;
            break;
        }
    }
    if (!suitableMemoryTypeFound) {
        std::cerr << "適切なメモリタイプが存在しません。" << std::endl;
        return -1;
    }

    vk::UniqueDeviceMemory indexBufMemory = device->allocateMemoryUnique(indexBufMemAllocInfo);

    device->bindBufferMemory(indexBuf.get(), indexBufMemory.get(), 0);

    {
        vk::BufferCreateInfo stagingBufferCreateInfo;
        stagingBufferCreateInfo.size = sizeof(uint16_t) * indices.size();
        stagingBufferCreateInfo.usage = vk::BufferUsageFlagBits::eTransferSrc;
        stagingBufferCreateInfo.sharingMode = vk::SharingMode::eExclusive;

        vk::UniqueBuffer stagingBuf = device->createBufferUnique(stagingBufferCreateInfo);

        vk::MemoryRequirements stagingBufMemReq = device->getBufferMemoryRequirements(stagingBuf.get());

        vk::MemoryAllocateInfo stagingBufMemAllocInfo;
        stagingBufMemAllocInfo.allocationSize = stagingBufMemReq.size;

        suitableMemoryTypeFound = false;
        for (uint32_t i = 0; i < memProps.memoryTypeCount; i++) {
            if (stagingBufMemReq.memoryTypeBits & (1 << i) && (memProps.memoryTypes[i].propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible)) {
                stagingBufMemAllocInfo.memoryTypeIndex = i;
                suitableMemoryTypeFound = true;
                break;
            }
        }
        if (!suitableMemoryTypeFound) {
            std::cerr << "適切なメモリタイプが存在しません。" << std::endl;
            return -1;
        }

        vk::UniqueDeviceMemory stagingBufMemory = device->allocateMemoryUnique(stagingBufMemAllocInfo);

        device->bindBufferMemory(stagingBuf.get(), stagingBufMemory.get(), 0);

        void *pStagingBufMem = device->mapMemory(stagingBufMemory.get(), 0, sizeof(uint16_t) * indices.size());

        std::memcpy(pStagingBufMem, indices.data(), sizeof(uint16_t) * indices.size());

        vk::MappedMemoryRange flushMemoryRange;
        flushMemoryRange.memory = stagingBufMemory.get();
        flushMemoryRange.offset = 0;
        flushMemoryRange.size = sizeof(uint16_t) * indices.size();

        device->flushMappedMemoryRanges({flushMemoryRange});

        device->unmapMemory(stagingBufMemory.get());

        vk::CommandPoolCreateInfo tmpCmdPoolCreateInfo;
        tmpCmdPoolCreateInfo.queueFamilyIndex = graphicsQueueFamilyIndex;
        tmpCmdPoolCreateInfo.flags = vk::CommandPoolCreateFlagBits::eTransient;
        vk::UniqueCommandPool tmpCmdPool = device->createCommandPoolUnique(tmpCmdPoolCreateInfo);

        vk::CommandBufferAllocateInfo tmpCmdBufAllocInfo;
        tmpCmdBufAllocInfo.commandPool = tmpCmdPool.get();
        tmpCmdBufAllocInfo.commandBufferCount = 1;
        tmpCmdBufAllocInfo.level = vk::CommandBufferLevel::ePrimary;
        std::vector<vk::UniqueCommandBuffer> tmpCmdBufs = device->allocateCommandBuffersUnique(tmpCmdBufAllocInfo);

        vk::BufferCopy bufCopy;
        bufCopy.srcOffset = 0;
        bufCopy.dstOffset = 0;
        bufCopy.size = sizeof(uint16_t) * indices.size();

        vk::CommandBufferBeginInfo cmdBeginInfo;
        cmdBeginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;

        tmpCmdBufs[0]->begin(cmdBeginInfo);
        tmpCmdBufs[0]->copyBuffer(stagingBuf.get(), indexBuf.get(), {bufCopy});
        tmpCmdBufs[0]->end();

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

        graphicsQueue.submit({submitInfo});
        graphicsQueue.waitIdle();
    }

    vk::BufferCreateInfo uniformBufferCreateInfo;
    uniformBufferCreateInfo.size = sizeof(SceneData);
    uniformBufferCreateInfo.usage = vk::BufferUsageFlagBits::eUniformBuffer;
    uniformBufferCreateInfo.sharingMode = vk::SharingMode::eExclusive;

    vk::UniqueBuffer uniformBuf = device->createBufferUnique(uniformBufferCreateInfo);

    vk::MemoryRequirements uniformBufMemReq = device->getBufferMemoryRequirements(uniformBuf.get());
        
    vk::MemoryAllocateInfo uniformBufMemAllocInfo;
    uniformBufMemAllocInfo.allocationSize = uniformBufMemReq.size;

    suitableMemoryTypeFound = false;
    for (uint32_t i = 0; i < memProps.memoryTypeCount; i++) {
        if (uniformBufMemReq.memoryTypeBits & (1 << i) &&
            (memProps.memoryTypes[i].propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible)) {
            uniformBufMemAllocInfo.memoryTypeIndex = i;
            suitableMemoryTypeFound = true;
            break;
        }
    }
    if (!suitableMemoryTypeFound) {
        std::cerr << "適切なメモリタイプが存在しません。" << std::endl;
        return -1;
    }

    vk::UniqueDeviceMemory uniformBufMemory = device->allocateMemoryUnique(uniformBufMemAllocInfo);

    device->bindBufferMemory(uniformBuf.get(), uniformBufMemory.get(), 0);

    {
        void* pUniformBufMem = device->mapMemory(uniformBufMemory.get(), 0, sizeof(SceneData));

        std::memcpy(pUniformBufMem, &sceneData, sizeof(SceneData));

        vk::MappedMemoryRange flushMemoryRange;
        flushMemoryRange.memory = uniformBufMemory.get();
        flushMemoryRange.offset = 0;
        flushMemoryRange.size = sizeof(SceneData);

        device->flushMappedMemoryRanges({ flushMemoryRange });

        device->unmapMemory(uniformBufMemory.get());
    }

    int imgWidth, imgHeight, imgCh;
    auto pImgData = stbi_load("image.jpg", &imgWidth, &imgHeight, &imgCh, STBI_rgb_alpha);
    if (pImgData == nullptr) {
        std::cerr << "画像ファイルの読み込みに失敗しました。" << std::endl;
        return -1;
    }

    vk::ImageCreateInfo texImgCreateInfo;
    texImgCreateInfo.imageType = vk::ImageType::e2D;
    texImgCreateInfo.extent = vk::Extent3D(imgWidth, imgHeight, 1);
    texImgCreateInfo.mipLevels = 1;
    texImgCreateInfo.arrayLayers = 1;
    texImgCreateInfo.format = vk::Format::eR8G8B8A8Unorm;
    texImgCreateInfo.tiling = vk::ImageTiling::eOptimal;
    texImgCreateInfo.initialLayout = vk::ImageLayout::eUndefined;
    texImgCreateInfo.usage = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst;
    texImgCreateInfo.sharingMode = vk::SharingMode::eExclusive;
    texImgCreateInfo.samples = vk::SampleCountFlagBits::e1;

    vk::UniqueImage texImage = device->createImageUnique(texImgCreateInfo);

    vk::MemoryRequirements texImgMemReq = device->getImageMemoryRequirements(texImage.get());

    vk::MemoryAllocateInfo texImgMemAllocInfo;
    texImgMemAllocInfo.allocationSize = texImgMemReq.size;

    suitableMemoryTypeFound = false;
    for (size_t i = 0; i < memProps.memoryTypeCount; i++) {
        if (texImgMemReq.memoryTypeBits & (1 << i)) {
            texImgMemAllocInfo.memoryTypeIndex = i;
            suitableMemoryTypeFound = true;
            break;
        }
    }

    if (!suitableMemoryTypeFound) {
        std::cerr << "使用可能なメモリタイプがありません。" << std::endl;
        return -1;
    }

    vk::UniqueDeviceMemory texImgMem = device->allocateMemoryUnique(texImgMemAllocInfo);

    device->bindImageMemory(texImage.get(), texImgMem.get(), 0);

    {
        size_t imgDataSize = 4 * imgWidth * imgHeight;

        vk::BufferCreateInfo imgStagingBufferCreateInfo;
        imgStagingBufferCreateInfo.size = imgDataSize;
        imgStagingBufferCreateInfo.usage = vk::BufferUsageFlagBits::eTransferSrc;
        imgStagingBufferCreateInfo.sharingMode = vk::SharingMode::eExclusive;

        vk::UniqueBuffer imgStagingBuf = device->createBufferUnique(imgStagingBufferCreateInfo);

        vk::MemoryRequirements imgStagingBufMemReq = device->getBufferMemoryRequirements(imgStagingBuf.get());

        vk::MemoryAllocateInfo imgStagingBufMemAllocInfo;
        imgStagingBufMemAllocInfo.allocationSize = imgStagingBufMemReq.size;

        suitableMemoryTypeFound = false;
        for (uint32_t i = 0; i < memProps.memoryTypeCount; i++) {
            if (imgStagingBufMemReq.memoryTypeBits & (1 << i) &&
                (memProps.memoryTypes[i].propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible)) {
                imgStagingBufMemAllocInfo.memoryTypeIndex = i;
                suitableMemoryTypeFound = true;
                break;
            }
        }
        if (!suitableMemoryTypeFound) {
            std::cerr << "適切なメモリタイプが存在しません。" << std::endl;
            return -1;
        }

        vk::UniqueDeviceMemory imgStagingBufMemory = device->allocateMemoryUnique(imgStagingBufMemAllocInfo);

        device->bindBufferMemory(imgStagingBuf.get(), imgStagingBufMemory.get(), 0);

        void *pImgStagingBufMem = device->mapMemory(imgStagingBufMemory.get(), 0, imgDataSize);

        std::memcpy(pImgStagingBufMem, pImgData, imgDataSize);

        vk::MappedMemoryRange flushMemoryRange;
        flushMemoryRange.memory = imgStagingBufMemory.get();
        flushMemoryRange.offset = 0;
        flushMemoryRange.size = imgDataSize;

        device->flushMappedMemoryRanges({flushMemoryRange});

        device->unmapMemory(imgStagingBufMemory.get());
        
        stbi_image_free(pImgData);

        vk::CommandPoolCreateInfo tmpCmdPoolCreateInfo;
        tmpCmdPoolCreateInfo.queueFamilyIndex = graphicsQueueFamilyIndex;
        tmpCmdPoolCreateInfo.flags = vk::CommandPoolCreateFlagBits::eTransient;
        vk::UniqueCommandPool tmpCmdPool = device->createCommandPoolUnique(tmpCmdPoolCreateInfo);

        vk::CommandBufferAllocateInfo tmpCmdBufAllocInfo;
        tmpCmdBufAllocInfo.commandPool = tmpCmdPool.get();
        tmpCmdBufAllocInfo.commandBufferCount = 1;
        tmpCmdBufAllocInfo.level = vk::CommandBufferLevel::ePrimary;
        std::vector<vk::UniqueCommandBuffer> tmpCmdBufs = device->allocateCommandBuffersUnique(tmpCmdBufAllocInfo);

        vk::CommandBufferBeginInfo cmdBeginInfo;
        cmdBeginInfo.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit;

        tmpCmdBufs[0]->begin(cmdBeginInfo);

        {
            vk::ImageMemoryBarrier barrior;
            barrior.oldLayout = vk::ImageLayout::eUndefined;
            barrior.newLayout = vk::ImageLayout::eTransferDstOptimal;
            barrior.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
            barrior.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
            barrior.image = texImage.get();
            barrior.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
            barrior.subresourceRange.baseMipLevel = 0;
            barrior.subresourceRange.levelCount = 1;
            barrior.subresourceRange.baseArrayLayer = 0;
            barrior.subresourceRange.layerCount = 1;
            barrior.srcAccessMask = {};
            barrior.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
            tmpCmdBufs[0]->pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTransfer, {}, {}, {}, {barrior});
        }

        vk::BufferImageCopy imgCopyRegion;
        imgCopyRegion.bufferOffset = 0;
        imgCopyRegion.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
        imgCopyRegion.imageSubresource.mipLevel = 0;
        imgCopyRegion.imageSubresource.baseArrayLayer = 0;
        imgCopyRegion.imageSubresource.layerCount = 1;
        imgCopyRegion.imageOffset = vk::Offset3D{0, 0, 0};
        imgCopyRegion.imageExtent = vk::Extent3D{uint32_t(imgWidth), uint32_t(imgHeight), 1};

        imgCopyRegion.bufferRowLength = 0;
        imgCopyRegion.bufferImageHeight = 0;

        tmpCmdBufs[0]->copyBufferToImage(imgStagingBuf.get(), texImage.get(), vk::ImageLayout::eTransferDstOptimal, { imgCopyRegion });

        {
            vk::ImageMemoryBarrier barrior;
            barrior.oldLayout = vk::ImageLayout::eTransferDstOptimal;
            barrior.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
            barrior.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
            barrior.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
            barrior.image = texImage.get();
            barrior.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
            barrior.subresourceRange.baseMipLevel = 0;
            barrior.subresourceRange.levelCount = 1;
            barrior.subresourceRange.baseArrayLayer = 0;
            barrior.subresourceRange.layerCount = 1;
            barrior.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
            barrior.dstAccessMask = vk::AccessFlagBits::eShaderRead;
            tmpCmdBufs[0]->pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader, {}, {}, {}, {barrior});
        }
        tmpCmdBufs[0]->end();
        
        vk::CommandBuffer submitCmdBuf[1] = {tmpCmdBufs[0].get()};
        vk::SubmitInfo submitInfo;
        submitInfo.commandBufferCount = 1;
        submitInfo.pCommandBuffers = submitCmdBuf;

        graphicsQueue.submit({submitInfo});
        graphicsQueue.waitIdle();
    }

    vk::SamplerCreateInfo samplerCreateInfo;
    samplerCreateInfo.magFilter = vk::Filter::eLinear;
    samplerCreateInfo.minFilter = vk::Filter::eLinear;
    samplerCreateInfo.addressModeU = vk::SamplerAddressMode::eRepeat;
    samplerCreateInfo.addressModeV = vk::SamplerAddressMode::eRepeat;
    samplerCreateInfo.addressModeW = vk::SamplerAddressMode::eRepeat;
    samplerCreateInfo.anisotropyEnable = false;
    samplerCreateInfo.maxAnisotropy = 1.0f;
    samplerCreateInfo.borderColor = vk::BorderColor::eIntOpaqueBlack;
    samplerCreateInfo.unnormalizedCoordinates = false;
    samplerCreateInfo.compareEnable = false;
    samplerCreateInfo.compareOp = vk::CompareOp::eAlways;
    samplerCreateInfo.mipmapMode = vk::SamplerMipmapMode::eLinear;
    samplerCreateInfo.mipLodBias = 0.0f;
    samplerCreateInfo.minLod = 0.0f;
    samplerCreateInfo.maxLod = 0.0f;
    vk::UniqueSampler sampler = device->createSamplerUnique(samplerCreateInfo);

    vk::ImageViewCreateInfo texImgViewCreateInfo;
    texImgViewCreateInfo.image = texImage.get();
    texImgViewCreateInfo.viewType = vk::ImageViewType::e2D;
    texImgViewCreateInfo.format = vk::Format::eR8G8B8A8Unorm;
    texImgViewCreateInfo.components.r = vk::ComponentSwizzle::eIdentity;
    texImgViewCreateInfo.components.g = vk::ComponentSwizzle::eIdentity;
    texImgViewCreateInfo.components.b = vk::ComponentSwizzle::eIdentity;
    texImgViewCreateInfo.components.a = vk::ComponentSwizzle::eIdentity;
    texImgViewCreateInfo.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
    texImgViewCreateInfo.subresourceRange.baseMipLevel = 0;
    texImgViewCreateInfo.subresourceRange.levelCount = 1;
    texImgViewCreateInfo.subresourceRange.baseArrayLayer = 0;
    texImgViewCreateInfo.subresourceRange.layerCount = 1;
    vk::UniqueImageView texImageView = device->createImageViewUnique(texImgViewCreateInfo);

    vk::DescriptorSetLayoutBinding descSetLayoutBinding[2];
    descSetLayoutBinding[0].binding = 0;
    descSetLayoutBinding[0].descriptorType = vk::DescriptorType::eUniformBuffer;
    descSetLayoutBinding[0].descriptorCount = 1;
    descSetLayoutBinding[0].stageFlags = vk::ShaderStageFlagBits::eVertex;
    descSetLayoutBinding[1].binding = 1;
    descSetLayoutBinding[1].descriptorType = vk::DescriptorType::eCombinedImageSampler;
    descSetLayoutBinding[1].descriptorCount = 1;
    descSetLayoutBinding[1].stageFlags = vk::ShaderStageFlagBits::eFragment;

    vk::DescriptorSetLayoutCreateInfo descSetLayoutCreateInfo{};
    descSetLayoutCreateInfo.bindingCount = std::size(descSetLayoutBinding);
    descSetLayoutCreateInfo.pBindings = descSetLayoutBinding;

    vk::UniqueDescriptorSetLayout descSetLayout = device->createDescriptorSetLayoutUnique(descSetLayoutCreateInfo);

    vk::DescriptorPoolSize descPoolSize[2];
    descPoolSize[0].type = vk::DescriptorType::eUniformBuffer;
    descPoolSize[0].descriptorCount = 1;
    descPoolSize[1].type = vk::DescriptorType::eCombinedImageSampler;
    descPoolSize[1].descriptorCount = 1;

    vk::DescriptorPoolCreateInfo descPoolCreateInfo;
    descPoolCreateInfo.poolSizeCount = std::size(descPoolSize);
    descPoolCreateInfo.pPoolSizes = descPoolSize;
    descPoolCreateInfo.maxSets = 1;

    vk::UniqueDescriptorPool descPool = device->createDescriptorPoolUnique(descPoolCreateInfo);

    vk::DescriptorSetAllocateInfo descSetAllocInfo;

    auto descSetLayouts = { descSetLayout.get() };

    descSetAllocInfo.descriptorPool = descPool.get();
    descSetAllocInfo.descriptorSetCount = descSetLayouts.size();
    descSetAllocInfo.pSetLayouts = descSetLayouts.begin();

    std::vector<vk::UniqueDescriptorSet> descSets = device->allocateDescriptorSetsUnique(descSetAllocInfo);

    vk::WriteDescriptorSet writeDescSet;
    writeDescSet.dstSet = descSets[0].get();
    writeDescSet.dstBinding = 0;
    writeDescSet.dstArrayElement = 0;
    writeDescSet.descriptorType = vk::DescriptorType::eUniformBuffer;

    vk::DescriptorBufferInfo descBufInfo[1];
    descBufInfo[0].buffer = uniformBuf.get();
    descBufInfo[0].offset = 0;
    descBufInfo[0].range = sizeof(SceneData);

    writeDescSet.descriptorCount = 1;
    writeDescSet.pBufferInfo = descBufInfo;

    device->updateDescriptorSets({ writeDescSet }, {});

    vk::WriteDescriptorSet writeTexDescSet;
    writeTexDescSet.dstSet = descSets[0].get();
    writeTexDescSet.dstBinding = 1;
    writeTexDescSet.dstArrayElement = 0;
    writeTexDescSet.descriptorType = vk::DescriptorType::eCombinedImageSampler;

    vk::DescriptorImageInfo descImgInfo[1];
    descImgInfo[0].imageView = texImageView.get();
    descImgInfo[0].imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
    descImgInfo[0].sampler = sampler.get();

    writeTexDescSet.descriptorCount = std::size(descImgInfo);
    writeTexDescSet.pImageInfo = descImgInfo;

    device->updateDescriptorSets({ writeTexDescSet }, {});

    std::vector<vk::SurfaceFormatKHR> surfaceFormats = physicalDevice.getSurfaceFormatsKHR(surface.get());
    std::vector<vk::PresentModeKHR> surfacePresentModes = physicalDevice.getSurfacePresentModesKHR(surface.get());

    vk::SurfaceFormatKHR swapchainFormat = surfaceFormats[0];
    vk::PresentModeKHR swapchainPresentMode = surfacePresentModes[0];

    vk::AttachmentDescription attachments[1];
    attachments[0].format = swapchainFormat.format;
    attachments[0].samples = vk::SampleCountFlagBits::e1;
    attachments[0].loadOp = vk::AttachmentLoadOp::eClear;
    attachments[0].storeOp = vk::AttachmentStoreOp::eStore;
    attachments[0].stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
    attachments[0].stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
    attachments[0].initialLayout = vk::ImageLayout::eUndefined;
    attachments[0].finalLayout = vk::ImageLayout::ePresentSrcKHR;

    vk::AttachmentReference subpass0_attachmentRefs[1];
    subpass0_attachmentRefs[0].attachment = 0;
    subpass0_attachmentRefs[0].layout = vk::ImageLayout::eColorAttachmentOptimal;

    vk::SubpassDescription subpasses[1];
    subpasses[0].pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
    subpasses[0].colorAttachmentCount = 1;
    subpasses[0].pColorAttachments = subpass0_attachmentRefs;

    vk::RenderPassCreateInfo renderpassCreateInfo;
    renderpassCreateInfo.attachmentCount = 1;
    renderpassCreateInfo.pAttachments = attachments;
    renderpassCreateInfo.subpassCount = 1;
    renderpassCreateInfo.pSubpasses = subpasses;
    renderpassCreateInfo.dependencyCount = 0;
    renderpassCreateInfo.pDependencies = nullptr;

    vk::UniqueRenderPass renderpass = device->createRenderPassUnique(renderpassCreateInfo);

    vk::Viewport viewports[1];
    viewports[0].x = 0.0;
    viewports[0].y = 0.0;
    viewports[0].minDepth = 0.0;
    viewports[0].maxDepth = 1.0;
    viewports[0].width = screenWidth;
    viewports[0].height = screenHeight;

    vk::Rect2D scissors[1];
    scissors[0].offset = vk::Offset2D{0, 0};
    scissors[0].extent = vk::Extent2D{screenWidth, screenHeight};

    vk::PipelineViewportStateCreateInfo viewportState;
    viewportState.viewportCount = 1;
    viewportState.pViewports = viewports;
    viewportState.scissorCount = 1;
    viewportState.pScissors = scissors;

    vk::VertexInputBindingDescription vertexBindingDescription[1];
    vertexBindingDescription[0].binding = 0;
    vertexBindingDescription[0].stride = sizeof(Vertex);
    vertexBindingDescription[0].inputRate = vk::VertexInputRate::eVertex;

    vk::VertexInputAttributeDescription vertexInputDescription[3];
    vertexInputDescription[0].binding = 0;
    vertexInputDescription[0].location = 0;
    vertexInputDescription[0].format = vk::Format::eR32G32Sfloat;
    vertexInputDescription[0].offset = offsetof(Vertex, pos);
    vertexInputDescription[1].binding = 0;
    vertexInputDescription[1].location = 1;
    vertexInputDescription[1].format = vk::Format::eR32G32B32Sfloat;
    vertexInputDescription[1].offset = offsetof(Vertex, color);
    vertexInputDescription[2].binding = 0;
    vertexInputDescription[2].location = 2;
    vertexInputDescription[2].format = vk::Format::eR32G32Sfloat;
    vertexInputDescription[2].offset = offsetof(Vertex, texUV);

    vk::PipelineVertexInputStateCreateInfo vertexInputInfo;
    vertexInputInfo.vertexBindingDescriptionCount = std::size(vertexBindingDescription);
    vertexInputInfo.pVertexBindingDescriptions = vertexBindingDescription;
    vertexInputInfo.vertexAttributeDescriptionCount = std::size(vertexInputDescription);
    vertexInputInfo.pVertexAttributeDescriptions = vertexInputDescription;

    vk::PipelineInputAssemblyStateCreateInfo inputAssembly;
    inputAssembly.topology = vk::PrimitiveTopology::eTriangleList;
    inputAssembly.primitiveRestartEnable = false;

    vk::PipelineRasterizationStateCreateInfo rasterizer;
    rasterizer.depthClampEnable = false;
    rasterizer.rasterizerDiscardEnable = false;
    rasterizer.polygonMode = vk::PolygonMode::eFill;
    rasterizer.lineWidth = 1.0f;
    rasterizer.cullMode = vk::CullModeFlagBits::eBack;
    rasterizer.frontFace = vk::FrontFace::eClockwise;
    rasterizer.depthBiasEnable = false;

    vk::PipelineMultisampleStateCreateInfo multisample;
    multisample.sampleShadingEnable = false;
    multisample.rasterizationSamples = vk::SampleCountFlagBits::e1;

    vk::PipelineColorBlendAttachmentState blendattachment[1];
    blendattachment[0].colorWriteMask = vk::ColorComponentFlagBits::eA | vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB;
    blendattachment[0].blendEnable = false;

    vk::PipelineColorBlendStateCreateInfo blend;
    blend.logicOpEnable = false;
    blend.attachmentCount = 1;
    blend.pAttachments = blendattachment;

    auto pipelineDescSetLayouts = { descSetLayout.get() };

    vk::PipelineLayoutCreateInfo layoutCreateInfo;
    layoutCreateInfo.setLayoutCount = pipelineDescSetLayouts.size();
    layoutCreateInfo.pSetLayouts = pipelineDescSetLayouts.begin();

    vk::UniquePipelineLayout pipelineLayout = device->createPipelineLayoutUnique(layoutCreateInfo);

    size_t vertSpvFileSz = std::filesystem::file_size("shader.vert.spv");

    std::ifstream vertSpvFile("shader.vert.spv", std::ios_base::binary);

    std::vector<char> vertSpvFileData(vertSpvFileSz);
    vertSpvFile.read(vertSpvFileData.data(), vertSpvFileSz);

    vk::ShaderModuleCreateInfo vertShaderCreateInfo;
    vertShaderCreateInfo.codeSize = vertSpvFileSz;
    vertShaderCreateInfo.pCode = reinterpret_cast<const uint32_t *>(vertSpvFileData.data());

    vk::UniqueShaderModule vertShader = device->createShaderModuleUnique(vertShaderCreateInfo);

    size_t fragSpvFileSz = std::filesystem::file_size("shader.frag.spv");

    std::ifstream fragSpvFile("shader.frag.spv", std::ios_base::binary);

    std::vector<char> fragSpvFileData(fragSpvFileSz);
    fragSpvFile.read(fragSpvFileData.data(), fragSpvFileSz);

    vk::ShaderModuleCreateInfo fragShaderCreateInfo;
    fragShaderCreateInfo.codeSize = fragSpvFileSz;
    fragShaderCreateInfo.pCode = reinterpret_cast<const uint32_t *>(fragSpvFileData.data());

    vk::UniqueShaderModule fragShader = device->createShaderModuleUnique(fragShaderCreateInfo);

    vk::PipelineShaderStageCreateInfo shaderStage[2];
    shaderStage[0].stage = vk::ShaderStageFlagBits::eVertex;
    shaderStage[0].module = vertShader.get();
    shaderStage[0].pName = "main";
    shaderStage[1].stage = vk::ShaderStageFlagBits::eFragment;
    shaderStage[1].module = fragShader.get();
    shaderStage[1].pName = "main";

    vk::GraphicsPipelineCreateInfo pipelineCreateInfo;
    pipelineCreateInfo.pViewportState = &viewportState;
    pipelineCreateInfo.pVertexInputState = &vertexInputInfo;
    pipelineCreateInfo.pInputAssemblyState = &inputAssembly;
    pipelineCreateInfo.pRasterizationState = &rasterizer;
    pipelineCreateInfo.pMultisampleState = &multisample;
    pipelineCreateInfo.pColorBlendState = &blend;
    pipelineCreateInfo.layout = pipelineLayout.get();
    pipelineCreateInfo.renderPass = renderpass.get();
    pipelineCreateInfo.subpass = 0;
    pipelineCreateInfo.stageCount = 2;
    pipelineCreateInfo.pStages = shaderStage;

    vk::UniquePipeline pipeline = device->createGraphicsPipelineUnique(nullptr, pipelineCreateInfo).value;

    vk::UniqueSwapchainKHR swapchain;
    std::vector<vk::Image> swapchainImages;
    std::vector<vk::UniqueImageView> swapchainImageViews;
    std::vector<vk::UniqueFramebuffer> swapchainFramebufs;

    auto recreateSwapchain = [&]() {
        swapchainFramebufs.clear();
        swapchainImageViews.clear();
        swapchainImages.clear();
        swapchain.reset();

        vk::SurfaceCapabilitiesKHR surfaceCapabilities = physicalDevice.getSurfaceCapabilitiesKHR(surface.get());

        vk::SwapchainCreateInfoKHR swapchainCreateInfo;
        swapchainCreateInfo.surface = surface.get();
        swapchainCreateInfo.minImageCount = surfaceCapabilities.minImageCount + 1;
        swapchainCreateInfo.imageFormat = swapchainFormat.format;
        swapchainCreateInfo.imageColorSpace = swapchainFormat.colorSpace;
        swapchainCreateInfo.imageExtent = surfaceCapabilities.currentExtent;
        swapchainCreateInfo.imageArrayLayers = 1;
        swapchainCreateInfo.imageUsage = vk::ImageUsageFlagBits::eColorAttachment;
        swapchainCreateInfo.imageSharingMode = vk::SharingMode::eExclusive;
        swapchainCreateInfo.preTransform = surfaceCapabilities.currentTransform;
        swapchainCreateInfo.presentMode = swapchainPresentMode;
        swapchainCreateInfo.clipped = VK_TRUE;

        swapchain = device->createSwapchainKHRUnique(swapchainCreateInfo);

        swapchainImages = device->getSwapchainImagesKHR(swapchain.get());

        swapchainImageViews.resize(swapchainImages.size());

        for (size_t i = 0; i < swapchainImages.size(); i++) {
            vk::ImageViewCreateInfo imgViewCreateInfo;
            imgViewCreateInfo.image = swapchainImages[i];
            imgViewCreateInfo.viewType = vk::ImageViewType::e2D;
            imgViewCreateInfo.format = swapchainFormat.format;
            imgViewCreateInfo.components.r = vk::ComponentSwizzle::eIdentity;
            imgViewCreateInfo.components.g = vk::ComponentSwizzle::eIdentity;
            imgViewCreateInfo.components.b = vk::ComponentSwizzle::eIdentity;
            imgViewCreateInfo.components.a = vk::ComponentSwizzle::eIdentity;
            imgViewCreateInfo.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
            imgViewCreateInfo.subresourceRange.baseMipLevel = 0;
            imgViewCreateInfo.subresourceRange.levelCount = 1;
            imgViewCreateInfo.subresourceRange.baseArrayLayer = 0;
            imgViewCreateInfo.subresourceRange.layerCount = 1;

            swapchainImageViews[i] = device->createImageViewUnique(imgViewCreateInfo);
        }

        swapchainFramebufs.resize(swapchainImages.size());

        for (size_t i = 0; i < swapchainImages.size(); i++) {
            vk::ImageView frameBufAttachments[1];
            frameBufAttachments[0] = swapchainImageViews[i].get();

            vk::FramebufferCreateInfo frameBufCreateInfo;
            frameBufCreateInfo.width = surfaceCapabilities.currentExtent.width;
            frameBufCreateInfo.height = surfaceCapabilities.currentExtent.height;
            frameBufCreateInfo.layers = 1;
            frameBufCreateInfo.renderPass = renderpass.get();
            frameBufCreateInfo.attachmentCount = 1;
            frameBufCreateInfo.pAttachments = frameBufAttachments;

            swapchainFramebufs[i] = device->createFramebufferUnique(frameBufCreateInfo);
        }
    };

    recreateSwapchain();

    vk::CommandPoolCreateInfo cmdPoolCreateInfo;
    cmdPoolCreateInfo.queueFamilyIndex = graphicsQueueFamilyIndex;
    cmdPoolCreateInfo.flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
    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::SemaphoreCreateInfo semaphoreCreateInfo;

    vk::UniqueSemaphore swapchainImgSemaphore, imgRenderedSemaphore;
    swapchainImgSemaphore = device->createSemaphoreUnique(semaphoreCreateInfo);
    imgRenderedSemaphore = device->createSemaphoreUnique(semaphoreCreateInfo);

    vk::FenceCreateInfo fenceCreateInfo;
    fenceCreateInfo.flags = vk::FenceCreateFlagBits::eSignaled;

    vk::UniqueFence imgRenderedFence = device->createFenceUnique(fenceCreateInfo);

    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();

        device->waitForFences({imgRenderedFence.get()}, VK_TRUE, UINT64_MAX);

        vk::ResultValue acquireImgResult = device->acquireNextImageKHR(swapchain.get(), 1'000'000'000, swapchainImgSemaphore.get());
        if (acquireImgResult.result == vk::Result::eSuboptimalKHR || acquireImgResult.result == vk::Result::eErrorOutOfDateKHR) {
            std::cerr << "スワップチェーンを再作成します。" << std::endl;
            recreateSwapchain();
            continue;
        }
        if (acquireImgResult.result != vk::Result::eSuccess) {
            std::cerr << "次フレームの取得に失敗しました。" << std::endl;
            return -1;
        }
        device->resetFences({imgRenderedFence.get()});

        uint32_t imgIndex = acquireImgResult.value;

        cmdBufs[0]->reset();

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

        vk::ClearValue clearVal[1];
        clearVal[0].color.float32[0] = 0.0f;
        clearVal[0].color.float32[1] = 0.0f;
        clearVal[0].color.float32[2] = 0.0f;
        clearVal[0].color.float32[3] = 1.0f;

        vk::RenderPassBeginInfo renderpassBeginInfo;
        renderpassBeginInfo.renderPass = renderpass.get();
        renderpassBeginInfo.framebuffer = swapchainFramebufs[imgIndex].get();
        renderpassBeginInfo.renderArea = vk::Rect2D({0, 0}, {screenWidth, screenHeight});
        renderpassBeginInfo.clearValueCount = 1;
        renderpassBeginInfo.pClearValues = clearVal;

        cmdBufs[0]->beginRenderPass(renderpassBeginInfo, vk::SubpassContents::eInline);

        cmdBufs[0]->bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.get());
        cmdBufs[0]->bindVertexBuffers(0, {vertexBuf.get()}, {0});
        cmdBufs[0]->bindIndexBuffer(indexBuf.get(), 0, vk::IndexType::eUint16);
        cmdBufs[0]->bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout.get(), 0, { descSets[0].get() }, {});
        cmdBufs[0]->drawIndexed(indices.size(), 1, 0, 0, 0);

        cmdBufs[0]->endRenderPass();

        cmdBufs[0]->end();

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

        vk::Semaphore renderwaitSemaphores[] = {swapchainImgSemaphore.get()};
        vk::PipelineStageFlags renderwaitStages[] = {vk::PipelineStageFlagBits::eColorAttachmentOutput};
        submitInfo.waitSemaphoreCount = 1;
        submitInfo.pWaitSemaphores = renderwaitSemaphores;
        submitInfo.pWaitDstStageMask = renderwaitStages;

        vk::Semaphore renderSignalSemaphores[] = {imgRenderedSemaphore.get()};
        submitInfo.signalSemaphoreCount = 1;
        submitInfo.pSignalSemaphores = renderSignalSemaphores;

        graphicsQueue.submit({submitInfo}, imgRenderedFence.get());

        vk::PresentInfoKHR presentInfo;

        auto presentSwapchains = {swapchain.get()};
        auto imgIndices = {imgIndex};

        presentInfo.swapchainCount = presentSwapchains.size();
        presentInfo.pSwapchains = presentSwapchains.begin();
        presentInfo.pImageIndices = imgIndices.begin();

        vk::Semaphore presenWaitSemaphores[] = {imgRenderedSemaphore.get()};
        presentInfo.waitSemaphoreCount = 1;
        presentInfo.pWaitSemaphores = presenWaitSemaphores;

        graphicsQueue.presentKHR(presentInfo);
    }

    graphicsQueue.waitIdle();
    glfwTerminate();
    return 0;
}
#version 450
#extension GL_ARB_separate_shader_objects : enable

layout(set = 0, binding = 0) uniform SceneData {
    vec2 rectCenter;
} sceneData;

layout(location = 0) in vec2 inPos;
layout(location = 1) in vec3 inColor;
layout(location = 2) in vec2 inTexUV;
layout(location = 0) out vec3 fragmentColor;
layout(location = 1) out vec2 fragmentTexUV;

void main() {
    gl_Position = vec4(sceneData.rectCenter + inPos, 0.0, 1.0);
    fragmentColor = inColor;
    fragmentTexUV = inTexUV;
}
#version 450
#extension GL_ARB_separate_shader_objects : enable

layout(set = 0, binding = 1) uniform sampler2D texSampler;

layout(location = 0) in vec3 fragmentColor;
layout(location = 1) in vec2 fragmentTexUV;
layout(location = 0) out vec4 outColor;

void main() {
    outColor = texture(texSampler, fragmentTexUV);
}
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})

find_package(glfw3 CONFIG REQUIRED)
target_link_libraries(app PRIVATE glfw)

find_package(Stb REQUIRED)
target_include_directories(app PRIVATE ${Stb_INCLUDE_DIR})