2017年11月13日月曜日

Learning of Vulkan Part 01

はじめに

Vulkanを段階的に, 私のために学習していきます.
私のための学習ですので, プログラミング, CGの基礎, 概要は省略します.
画面のクリアまで行いました.

IntelのチュートリアルAPI without Secretsを参考にしています.
コードはGitHubに置くことにします.
共通コードは, 毎回, 設計に悩んで変更・リファクタをすることになると思います.

概要

今回の流れは, 以下のように画面のクリアしてループするところまで試します.
  • サーフェスの作成
  • キューの作成
  • スワップチェインの作成
  • コマンドバッファの作成
  • コマンドバッファにクリアコマンドを積んで実行

サーフェスの作成

ここで指すサーフェスは実際に出力する対象を抽象化したものです.
各ウィンドウシステムで作成したウィンドウをサーフェスに割り当てます.
WGL, GLXやEGLが統一されたと思えばいいでしょうか.
#ifdef VK_USE_PLATFORM_WIN32_KHR
vk::Instance::SurfaceCreateInfo surfaceCreateInfo =
{
    VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, //structure type
    VLK_NULL,
    0, //flags
    window.getInstance(),
    window.getHandle(),
};
VkResult result = vkCreateWin32SurfaceKHR(&createInfo, allocator_, &presentSurface_);
#endif

キューの作成

キューはコマンドをためる場所です. GPUがこのキューからワークを取り出し実行します.
グラフィックス用やコンピューティング用を分けたり, それぞれの種類毎に複数のキューを持ったり,
実用するときに割り当てを再考する必要があると思います.
vk::u32 numQueueFamilies = 1;
vk::u32 numGraphicsQueues = 1;
if(graphicsQueueFamily == presentQueueFamily){
    ++numGraphicsQueues;
}

VkDeviceQueueCreateInfo queueCreateInfo[vk::MaxQueues];
const float queuePriorities[] = {0.0f, 0.1f, 0.2f, 0.3f, 0.4f, 0.5f, 0.6f, 0.7f};
queueCreateInfo[0] = {
    VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, //structure type
    VLK_NULL,
    0, //device queue creation flags
    graphicsQueueFamily, //selected queue family's index
    numGraphicsQueues, //queue count
    queuePriorities, //queue priorities
};
if(graphicsQueueFamily != presentQueueFamily){
    queueCreateInfo[1] = {
        VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, //structure type
        VLK_NULL,
        0, //device queue creation flags
        presentQueueFamily, //selected queue family's index
        1, //queue count
        queuePriorities, //queue priorities
    };
}

スワップチェインの作成

スワップの方法, フォーマット, サイズ, 色空間を指定します.
色空間の選択や自動で変換が入るのかなど調査しておくことが課題です.
bool selectPresentMode(
    VkSurfaceFormatKHR& selectedFormat,
    VkPresentModeKHR& selectedMode,
    vk::u32 width,
    vk::u32 height,
    vk::u32 swapChainImages,
    VkImageUsageFlags supportedFlags,
    VkSurfaceTransformFlagsKHR supportedTransform,
    VkFormat format,
    VkColorSpaceKHR colorSpace,
    VkPresentModeKHR mode,
    vk::Instance& instance,
    vk::PhysicalDevice& physicalDevice)
{
    static const vk::u32 MaxSurfaceFormats = 16;
    static const vk::u32 MaxPresentModes = 8;
    VkSurfaceCapabilitiesKHR surfaceCapabilities;
    vk::u32 numSurfaceFormats = 0;
    vk::u32 numPresentModes = 0;
    VkSurfaceFormatKHR surfaceFormats[MaxSurfaceFormats];
    VkPresentModeKHR presentModes[MaxPresentModes];

    VkSurfaceKHR surface = instance.getPresentSurface();
    if(VK_SUCCESS != instance.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfaceCapabilities)){
        return false;
    }
    if(width<surfaceCapabilities.minImageExtent.width || surfaceCapabilities.maxImageExtent.width<width){
        return false;
    }
    if(height<surfaceCapabilities.minImageExtent.height || surfaceCapabilities.maxImageExtent.height<height){
        return false;
    }
    if(swapChainImages < surfaceCapabilities.minImageCount || surfaceCapabilities.maxImageCount < swapChainImages){
        return false;
    }

    //supported flags
    //---------------------------------------------------------------
    if(supportedFlags != (surfaceCapabilities.supportedUsageFlags & supportedFlags)){
        return false;
    }

    //supported transform
    //---------------------------------------------------------------
    if(supportedTransform != (surfaceCapabilities.supportedTransforms & supportedTransform)){
        return false;
    }

    selectedFormat.format = VK_FORMAT_UNDEFINED;
    instance.vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &numSurfaceFormats, VLK_NULL);
    numSurfaceFormats = vk::minimum(numSurfaceFormats, MaxSurfaceFormats);
    instance.vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &numSurfaceFormats, surfaceFormats);
    for(vk::u32 i=0; i<numSurfaceFormats; ++i){
        if(surfaceFormats[i].format == format && surfaceFormats[i].colorSpace == colorSpace){
            selectedFormat = surfaceFormats[i];
            break;
        }
    }
    if(VK_FORMAT_UNDEFINED == selectedFormat.format){
        return false;
    }

    selectedMode = VK_PRESENT_MODE_MAX_ENUM_KHR;
    instance.vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &numPresentModes, VLK_NULL);
    numPresentModes = vk::minimum(numPresentModes, MaxPresentModes);
    instance.vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &numPresentModes, presentModes);
    for(vk::u32 i=0; i<numPresentModes; ++i){
        if(presentModes[i] == mode){
            selectedMode = presentModes[i];
            break;
        }
    }
    if(VK_PRESENT_MODE_MAX_ENUM_KHR == selectedMode){
        return false;
    }
    return true;
}

コマンドバッファの作成

とくに設定することは多くないのですが, キューと合わせて管理方法を考える必要がありそうです.
VkResult result;

VkCommandPoolCreateInfo commandPoolCreateInfo =
{
    VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
    VLK_NULL,
    0,
    queueFamilyIndices_[PresentQueue],
};
    if(VK_SUCCESS != (result = vkCreateCommandPool(device_, &commandPoolCreateInfo, allocator_, &presentQueueCommandPool_))){
        return result;
    }

VkCommandBufferAllocateInfo commandBufferAllocateInfo =
{
    VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
    VLK_NULL,
    presentQueueCommandPool_,
    VK_COMMAND_BUFFER_LEVEL_PRIMARY,
    swapchainImageCount_,
};
if(VK_SUCCESS != (result = vkAllocateCommandBuffers(device_, &commandBufferAllocateInfo, presentQueueCommandBuffers_))){
    return result;
}

クリアコマンド実行

コマンドをバッファにプッシュして, 実行します.
とりあえずクリアコマンドだけです.
VkResult result;
if(VK_SUCCESS != (result = vkGetSwapchainImagesKHR_(device_, swapchain_, &swapchainImageCount_, swapchainImages_))){
    return result;
}
VkCommandBufferBeginInfo commandBufferBeginInfo =
{
    VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, //structure type
    VLK_NULL,
    VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, //flags
    VLK_NULL,
};
VkImageSubresourceRange imageSubresourceRange =
{
    VK_IMAGE_ASPECT_COLOR_BIT, //flags
    0, 1, 0, 1,
};

VkImageMemoryBarrier barrierFromPresentToClear =
{
    VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, //structure type
    VLK_NULL,
    VK_ACCESS_MEMORY_READ_BIT,
    VK_ACCESS_TRANSFER_WRITE_BIT,
    VK_IMAGE_LAYOUT_UNDEFINED,
    VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
    VK_QUEUE_FAMILY_IGNORED,
    VK_QUEUE_FAMILY_IGNORED,
    VLK_NULL,
    imageSubresourceRange,
};

VkImageMemoryBarrier barrierFromClearToPresent =
{
    VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, //structure type
    VLK_NULL,
    VK_ACCESS_TRANSFER_WRITE_BIT,
    VK_ACCESS_MEMORY_READ_BIT,
    VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
    VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
    VK_QUEUE_FAMILY_IGNORED,
    VK_QUEUE_FAMILY_IGNORED,
    VLK_NULL,
    imageSubresourceRange,
};

for(u32 i=0; i<swapchainImageCount_; ++i){
    barrierFromPresentToClear.image = swapchainImages_[i];
    barrierFromClearToPresent.image = swapchainImages_[i];

    vkBeginCommandBuffer(presentQueueCommandBuffers_[i], &commandBufferBeginInfo);
    vkCmdPipelineBarrier(
        presentQueueCommandBuffers_[i],
        VK_PIPELINE_STAGE_TRANSFER_BIT,
        VK_PIPELINE_STAGE_TRANSFER_BIT,
        0, 0,
        VLK_NULL,
        0,
        VLK_NULL,
        1,
        &barrierFromPresentToClear);

    vkCmdClearColorImage(
        presentQueueCommandBuffers_[i],
        swapchainImages_[i],
        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
        &clearColor_,
        1,
        &imageSubresourceRange);
    vkCmdPipelineBarrier(
        presentQueueCommandBuffers_[i],
        VK_PIPELINE_STAGE_TRANSFER_BIT,
        VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
        0, 0,
        VLK_NULL,
        0,
        VLK_NULL,
        1,
        &barrierFromClearToPresent);
    if(VK_SUCCESS != (result = vkEndCommandBuffer(presentQueueCommandBuffers_[i]))){
        return result;
    }
 }
VkResult result;
u32 imageIndex;
result = vkAcquireNextImageKHR_(device_, swapchain_, UINT64_MAX, semaphoreImageAvailable_, VLK_NULL, &imageIndex);
switch(result)
{
case VK_SUCCESS:
case VK_SUBOPTIMAL_KHR:
    break;
case VK_ERROR_OUT_OF_DATE_KHR:
    return onWindowSizeChanged();
default:
    return false;
}

VkPipelineStageFlags waitDstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
VkSubmitInfo submitInfo =
{
    VK_STRUCTURE_TYPE_SUBMIT_INFO, //structure type
    VLK_NULL,
    1,
    &semaphoreImageAvailable_,
    &waitDstStageMask,
    1,
    &presentQueueCommandBuffers_[imageIndex],
    1,
    &semaphoreRenderingFinished_,
};
if(VK_SUCCESS != (result = vkQueueSubmit(queues_[PresentQueue], 1, &submitInfo, VLK_NULL))){
    return false;
}

VkPresentInfoKHR presentInfo =
{
    VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
    VLK_NULL,
    1,
    &semaphoreRenderingFinished_,
    1,
    &swapchain_,
    &imageIndex,
    VLK_NULL,
};
result = vkQueuePresentKHR_(queues_[PresentQueue], &presentInfo);
switch(result)
{
case VK_SUCCESS:
    break;
case VK_ERROR_OUT_OF_DATE_KHR:
case VK_SUBOPTIMAL_KHR:
    return onWindowSizeChanged();
default:
    return false;
}
return true;

まとめ

バッファのクリアしループまで行いました.
コマンドバッファやキューなど今までのOpenGLとの違いがあり, どうやって管理していくか悩む箇所が出てきました.

0 件のコメント:

コメントを投稿