Program Listing for File VulkanImage.cpp

Return to documentation for file (Src/GraphicsEngineVulkan/vulkan_base/VulkanImage.cpp)

#include "vulkan_base/VulkanImage.hpp"

#include "common/MemoryHelper.hpp"
#include "common/Utilities.hpp"

Kataglyphis::VulkanImage::VulkanImage() {}

void Kataglyphis::VulkanImage::create(VulkanDevice *device,
  uint32_t width,
  uint32_t height,
  uint32_t mip_levels,
  VkFormat format,
  VkImageTiling tiling,
  VkImageUsageFlags use_flags,
  VkMemoryPropertyFlags prop_flags)
{
    this->device = device;
    // CREATE image
    // image creation info
    VkImageCreateInfo image_create_info{};
    image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
    image_create_info.imageType = VK_IMAGE_TYPE_2D;// type of image (1D, 2D, 3D)
    image_create_info.extent.width = width;// width if image extent
    image_create_info.extent.height = height;// height if image extent
    image_create_info.extent.depth = 1;// height if image extent
    image_create_info.mipLevels = mip_levels;// number of mipmap levels
    image_create_info.arrayLayers = 1;// number of levels in image array
    image_create_info.format = format;// format type of image
    image_create_info.tiling = tiling;// tiling of image ("arranged" for optimal reading)
    image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;// layout of image data on creation
    image_create_info.usage = use_flags;// bit flags defining what image will be used for
    image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;// number of samples for multisampling
    image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;// whether image can be shared between queues

    VkResult result = vkCreateImage(device->getLogicalDevice(), &image_create_info, nullptr, &image);
    ASSERT_VULKAN(result, "Failed to create an image!")

    // CREATE memory for image
    // get memory requirements for a type of image
    VkMemoryRequirements memory_requirements;
    vkGetImageMemoryRequirements(device->getLogicalDevice(), image, &memory_requirements);

    // allocate memory using image requirements and user defined properties
    VkMemoryAllocateInfo memory_alloc_info{};
    memory_alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    memory_alloc_info.allocationSize = memory_requirements.size;
    memory_alloc_info.memoryTypeIndex =
      Kataglyphis::find_memory_type_index(device->getPhysicalDevice(), memory_requirements.memoryTypeBits, prop_flags);

    result = vkAllocateMemory(device->getLogicalDevice(), &memory_alloc_info, nullptr, &imageMemory);
    ASSERT_VULKAN(result, "Failed to allocate memory!")

    // connect memory to image
    vkBindImageMemory(device->getLogicalDevice(), image, imageMemory, 0);
}

void Kataglyphis::VulkanImage::transitionImageLayout(VkDevice device,
  VkQueue queue,
  VkCommandPool command_pool,
  VkImageLayout old_layout,
  VkImageLayout new_layout,
  VkImageAspectFlags aspectMask,
  uint32_t mip_levels)
{
    VkCommandBuffer command_buffer = commandBufferManager.beginCommandBuffer(device, command_pool);

    // VK_IMAGE_ASPECT_COLOR_BIT
    VkImageMemoryBarrier memory_barrier{};
    memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    memory_barrier.oldLayout = old_layout;
    memory_barrier.newLayout = new_layout;
    memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;// Queue family to transition from
    memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;// Queue family to transition to
    memory_barrier.image = image;// image being accessed and modified as part of barrier
    memory_barrier.subresourceRange.aspectMask = aspectMask;// aspect of image being altered
    memory_barrier.subresourceRange.baseMipLevel = 0;// first mip level to start alterations on
    memory_barrier.subresourceRange.levelCount = mip_levels;// number of mip levels to alter starting from baseMipLevel
    memory_barrier.subresourceRange.baseArrayLayer = 0;// first layer to start alterations on
    memory_barrier.subresourceRange.layerCount = 1;// number of layers to alter starting from baseArrayLayer

    // if transitioning from new image to image ready to receive data
    memory_barrier.srcAccessMask = accessFlagsForImageLayout(old_layout);
    memory_barrier.dstAccessMask = accessFlagsForImageLayout(new_layout);

    VkPipelineStageFlags src_stage = pipelineStageForLayout(old_layout);
    VkPipelineStageFlags dst_stage = pipelineStageForLayout(new_layout);

    vkCmdPipelineBarrier(

      command_buffer,
      src_stage,
      dst_stage,// pipeline stages (match to src and dst accessmask)
      0,// no dependency flags
      0,
      nullptr,// memory barrier count + data
      0,
      nullptr,// buffer memory barrier count + data
      1,
      &memory_barrier// image memory barrier count + data

    );

    commandBufferManager.endAndSubmitCommandBuffer(device, command_pool, queue, command_buffer);
}

void Kataglyphis::VulkanImage::transitionImageLayout(VkCommandBuffer command_buffer,
  VkImageLayout old_layout,
  VkImageLayout new_layout,
  uint32_t mip_levels,
  VkImageAspectFlags aspectMask)
{
    VkImageMemoryBarrier memory_barrier{};
    memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    memory_barrier.oldLayout = old_layout;
    memory_barrier.newLayout = new_layout;
    memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;// Queue family to transition from
    memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;// Queue family to transition to
    memory_barrier.image = image;// image being accessed and modified as part of barrier
    memory_barrier.subresourceRange.aspectMask = aspectMask;// aspect of image being altered
    memory_barrier.subresourceRange.baseMipLevel = 0;// first mip level to start alterations on
    memory_barrier.subresourceRange.levelCount = mip_levels;// number of mip levels to alter starting from baseMipLevel
    memory_barrier.subresourceRange.baseArrayLayer = 0;// first layer to start alterations on
    memory_barrier.subresourceRange.layerCount = 1;// number of layers to alter starting from baseArrayLayer

    memory_barrier.srcAccessMask = accessFlagsForImageLayout(old_layout);
    memory_barrier.dstAccessMask = accessFlagsForImageLayout(new_layout);

    VkPipelineStageFlags src_stage = pipelineStageForLayout(old_layout);
    VkPipelineStageFlags dst_stage = pipelineStageForLayout(new_layout);

    // if transitioning from new image to image ready to receive data

    vkCmdPipelineBarrier(

      command_buffer,
      src_stage,
      dst_stage,// pipeline stages (match to src and dst accessmask)
      0,// no dependency flags
      0,
      nullptr,// memory barrier count + data
      0,
      nullptr,// buffer memory barrier count + data
      1,
      &memory_barrier// image memory barrier count + data

    );
}

void Kataglyphis::VulkanImage::setImage(VkImage image) { this->image = image; }

void Kataglyphis::VulkanImage::cleanUp()
{
    vkDestroyImage(device->getLogicalDevice(), image, nullptr);
    vkFreeMemory(device->getLogicalDevice(), imageMemory, nullptr);
}

Kataglyphis::VulkanImage::~VulkanImage() {}

VkAccessFlags Kataglyphis::VulkanImage::accessFlagsForImageLayout(VkImageLayout layout)
{
    switch (layout) {
    case VK_IMAGE_LAYOUT_PREINITIALIZED:
        return VK_ACCESS_HOST_WRITE_BIT;
    case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
        return VK_ACCESS_TRANSFER_WRITE_BIT;
    case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
        return VK_ACCESS_TRANSFER_READ_BIT;
    case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
        return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
        return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
    case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
        return VK_ACCESS_SHADER_READ_BIT;
    default:
        return VkAccessFlags();
    }
}

VkPipelineStageFlags Kataglyphis::VulkanImage::pipelineStageForLayout(VkImageLayout oldImageLayout)
{
    switch (oldImageLayout) {
    case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
    case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
        return VK_PIPELINE_STAGE_TRANSFER_BIT;
    case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
        return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
        return VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;// We do this to allow queue
                                                  // other than graphic return
                                                  // VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
    case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
        return VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;// We do this to allow queue
                                                  // other than graphic return
                                                  // VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
    case VK_IMAGE_LAYOUT_PREINITIALIZED:
        return VK_PIPELINE_STAGE_HOST_BIT;
    case VK_IMAGE_LAYOUT_UNDEFINED:
        return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
    default:
        return VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
    }
}