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.

As a start, this engine will render a basic spinning box with uniform color. However, it will then expand to cover the full functionality of the game engine.

The code uses a Texture class in order to load textures for use in the game. The header and source file for this class look like this:

Header:

#pragma once

#include <vulkan/vulkan.h>
#include <string>

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

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

	VkImageView GetImageView() const;
	VkSampler GetSampler() const;

private:
	VkDevice device;
	VkImage image;
	VkDeviceMemory imageMemory;
	VkImageView imageView;
	VkSampler sampler;

	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);
	void CreateImageView(VkFormat format, VkImageAspectFlags aspectFlags, uint32_t mipLevels);
	void CreateSampler(uint32_t mipLevels);
	void CopyBufferToImage(VkBuffer buffer, uint32_t width, uint32_t height);

	// Additional helper functions for texture loading…
};


Source:

#include "Texture.h"
#include <iostream>

Texture::Texture()
	: device(VK_NULL_HANDLE), image(VK_NULL_HANDLE), imageMemory(VK_NULL_HANDLE), imageView(VK_NULL_HANDLE), sampler(VK_NULL_HANDLE)
{
}

Texture::~Texture()
{
	Cleanup();
}

void Texture::LoadFromFile(const std::string& filename, VkDevice device, VkPhysicalDevice physicalDevice, VkCommandPool commandPool, VkQueue graphicsQueue)
{
	this->device = device;

	// Load image from file and store it in a buffer
	// …

	// Create vkImage, copy buffer to image, and create imageView and sampler
	// …

	CreateImage(width, height, mipLevels, VK_SAMPLE_COUNT_1_BIT, format, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
	CreateImageView(format, VK_IMAGE_ASPECT_COLOR_BIT, mipLevels);
	CreateSampler(mipLevels);

	CopyBufferToImage(buffer, width, height);
}

void Texture::Cleanup()
{
	vkDestroySampler(device, sampler, nullptr);
	vkDestroyImageView(device, imageView, nullptr);
	vkDestroyImage(device, image, nullptr);
	vkFreeMemory(device, imageMemory, nullptr);
}

VkImageView Texture::GetImageView() const
{
	return imageView;
}

VkSampler Texture::GetSampler() const
{
	return sampler;
}

void Texture::CreateImage(uint32_t width, uint32_t height, uint32_t mipLevels, VkSampleCountFlagBits numSamples, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties)
{
	// Create VkImage object
	// …

	// Allocate and bind memory for the image
	// …

	// Transition the image layout to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
	// …
}

void Texture::TransitionImageLayout(VkImageLayout oldLayout, VkImageLayout newLayout)
{
	// Create a one-time-use command buffer and record image layout transition commands
	// …
}

void Texture::CreateImageView(VkFormat format, VkImageAspectFlags aspectFlags, uint32_t mipLevels)
{
	// Create VkImageView object
	// …
}

void Texture::CreateSampler(uint32_t mipLevels)
{
	// Create VkSampler object
	// …
}

void Texture::CopyBufferToImage(VkBuffer buffer, uint32_t width, uint32_t height)
{
	// Create a one-time-use command buffer and record buffer to image copy commands
	// …
}


What would the code for these two files look like if it was completed to use stb_image as the the image loading library?