I’m building a video game engine using C++ as the coding language and Vulkan for graphics. I am trying to set up a generic renderer using Vulkan that is flexible and will render objects based on a vector that is supplied to it. The renderer will also handle the creation of the window using GLFW and use GLM for all relevant math calls. I am using the ASSIMP library to load 3d models and animations.

I am in the process of converting the codebase from using vulkan.h to vulkan.hpp, as well as utilising RAII. I have updated the BufferUtils class and Texture class header to incorporate these elements. However I still need to update the Texture class source based on these updates. Here is some of the code:

BufferUtils.h:

#pragma once

#include <vulkan/vulkan.hpp>

namespace BufferUtils
{
    void CreateBuffer(
        vk::Device device, vk::PhysicalDevice physicalDevice,
        vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags properties,
        vk::Buffer& buffer, vk::DeviceMemory& bufferMemory);

    uint32_t FindMemoryType(vk::PhysicalDevice physicalDevice, uint32_t typeFilter, vk::MemoryPropertyFlags properties);

    void CopyBuffer(
        vk::Device device, vk::CommandPool commandPool, vk::Queue graphicsQueue,
        vk::Buffer srcBuffer, vk::Buffer dstBuffer, vk::DeviceSize size);
}

Old Texture.h:

#pragma once

#include <vulkan/vulkan.h>
#include "stb_image.h" // Include the stb_image header
#include "BufferUtils.h"
#include <string>

class Texture
{
public:
    Texture();
    ~Texture();

    void LoadFromFile(const std::string& filename, VkDevice device, VkPhysicalDevice physicalDevice, VkCommandPool commandPool, VkQueue graphicsQueue);
    VkImageView GetImageView() const;
    VkSampler GetSampler() const;
    void Cleanup();

private:
    VkDevice device;
    VkImage image;
    VkDeviceMemory imageMemory;
    VkImageView imageView;
    VkSampler sampler;
    VkPhysicalDevice physicalDevice;
    VkCommandPool commandPool;
    VkQueue graphicsQueue;
    bool initialized = false;

    void CreateImage(uint32_t width, uint32_t height, uint32_t mipLevels, VkSampleCountFlagBits numSamples,
        VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties);
    void TransitionImageLayout(VkImageLayout oldLayout, VkImageLayout newLayout,
        uint32_t mipLevels, VkSampleCountFlagBits numSamples);
    void CopyBufferToImage(VkBuffer buffer, uint32_t width, uint32_t height);
    void CreateImageView(VkFormat format, VkImageAspectFlags aspectFlags, uint32_t mipLevels);
    void CreateSampler(uint32_t mipLevels);
    void GenerateMipmaps(uint32_t width, uint32_t height, uint32_t mipLevels);
    VkCommandBuffer BeginSingleTimeCommands(VkCommandPool commandPool, VkDevice device);
    void EndSingleTimeCommands(VkCommandPool commandPool, VkQueue graphicsQueue, VkDevice device, VkCommandBuffer commandBuffer);
};

New Texture.h:

#pragma once

#include <vulkan/vulkan.hpp>
#include "stb_image.h" // Include the stb_image header
#include "BufferUtils.h"
#include <string>

class Texture
{
public:
    Texture();
    ~Texture();

    void LoadFromFile(const std::string& filename, vk::Device device, vk::PhysicalDevice physicalDevice, vk::CommandPool commandPool, vk::Queue graphicsQueue);
    vk::ImageView GetImageView() const;
    vk::Sampler GetSampler() const;
    void Cleanup();

private:
    vk::Device device;
    vk::Image image;
    vk::DeviceMemory imageMemory;
    vk::ImageView imageView;
    vk::Sampler sampler;
    vk::PhysicalDevice physicalDevice;
    vk::CommandPool commandPool;
    vk::Queue graphicsQueue;
    bool initialized = false;

    void CreateImage(uint32_t width, uint32_t height, uint32_t mipLevels, vk::SampleCountFlagBits numSamples,
        vk::Format format, vk::ImageTiling tiling, vk::ImageUsageFlags usage, vk::MemoryPropertyFlags properties);
    void TransitionImageLayout(vk::ImageLayout oldLayout, vk::ImageLayout newLayout,
        uint32_t mipLevels, vk::SampleCountFlagBits numSamples);
    void CopyBufferToImage(vk::Buffer buffer, uint32_t width, uint32_t height);
    void CreateImageView(vk::Format format, vk::ImageAspectFlags aspectFlags, uint32_t mipLevels);
    void CreateSampler(uint32_t mipLevels);
    void GenerateMipmaps(uint32_t width, uint32_t height, uint32_t mipLevels);
    vk::CommandBuffer BeginSingleTimeCommands(vk::CommandPool commandPool, vk::Device device);
    void EndSingleTimeCommands(vk::CommandPool commandPool, vk::Queue graphicsQueue, vk::Device device, vk::CommandBuffer commandBuffer);
};


A portion of Texture.cpp:

void Texture::CreateImage(uint32_t width, uint32_t height, uint32_t mipLevels, VkSampleCountFlagBits numSamples, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties)
{
    VkImageCreateInfo imageInfo{};
    imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
    imageInfo.imageType = VK_IMAGE_TYPE_2D;
    imageInfo.extent.width = width;
    imageInfo.extent.height = height;
    imageInfo.extent.depth = 1;
    imageInfo.mipLevels = mipLevels;
    imageInfo.arrayLayers = 1;
    imageInfo.format = format;
    imageInfo.tiling = tiling;
    imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    imageInfo.usage = usage;
    imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
    imageInfo.samples = numSamples;
    imageInfo.flags = 0;

    if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS)
    {
        throw std::runtime_error("Failed to create image!");
    }

    VkMemoryRequirements memRequirements;
    vkGetImageMemoryRequirements(device, image, &memRequirements);

    VkMemoryAllocateInfo allocInfo{};
    allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    allocInfo.allocationSize = memRequirements.size;
    allocInfo.memoryTypeIndex = BufferUtils::FindMemoryType(physicalDevice, memRequirements.memoryTypeBits, properties);

    if (vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS)
    {
        throw std::runtime_error("Failed to allocate image memory!");
    }

    vkBindImageMemory(device, image, imageMemory, 0);
}

void Texture::TransitionImageLayout(VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t mipLevels, VkSampleCountFlagBits numSamples)
{
    // Create a one-time-use command buffer
    VkCommandBufferAllocateInfo allocInfo{};
    allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
    allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
    allocInfo.commandPool = commandPool;
    allocInfo.commandBufferCount = 1;

    VkCommandBuffer commandBuffer;
    vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer);

    VkCommandBufferBeginInfo beginInfo{};
    beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
    beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;

    vkBeginCommandBuffer(commandBuffer, &beginInfo);

    VkImageMemoryBarrier barrier{};
    barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    barrier.oldLayout = oldLayout;
    barrier.newLayout = newLayout;
    barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    barrier.image = image;
    barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    barrier.subresourceRange.baseMipLevel = 0;
    barrier.subresourceRange.levelCount = mipLevels;
    barrier.subresourceRange.baseArrayLayer = 0;
    barrier.subresourceRange.layerCount = 1;

    VkPipelineStageFlags sourceStage;
    VkPipelineStageFlags destinationStage;

    if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
        barrier.srcAccessMask = 0;
        barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;

        sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
        destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
    }
    else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
        barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
        barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;

        sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
        destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
    }
    else {
        throw std::invalid_argument("Unsupported layout transition!");
    }

    vkCmdPipelineBarrier(
        commandBuffer,
        sourceStage, destinationStage,
        0,
        0, nullptr,
        0, nullptr,
        1, &barrier
    );

    // End command buffer recording
    vkEndCommandBuffer(commandBuffer);

    // Submit command buffer
    VkSubmitInfo submitInfo{};
    submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &commandBuffer;

    vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
    vkQueueWaitIdle(graphicsQueue);

    // Free command buffer
    vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
}

Can you please align update the code from Texture.cpp to align to the BufferUtils class and the new Texture header code?