Skip to content

Instantly share code, notes, and snippets.

@AregevDev
Created March 5, 2020 22:55
Show Gist options
  • Save AregevDev/eaf15a6092699841fb469fd13ce4343b to your computer and use it in GitHub Desktop.
Save AregevDev/eaf15a6092699841fb469fd13ce4343b to your computer and use it in GitHub Desktop.
Dummy Vulkan compute application
#version 450 core
layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout (set = 0, binding = 5, std430) buffer MyBuffer
{
uint array[];
} myBuffer;
layout (push_constant) uniform Scalar
{
uint x;
} scalar;
void main()
{
myBuffer.array[gl_GlobalInvocationID.x] = gl_GlobalInvocationID.x * scalar.x;
}
#include "volk.h"
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#define BUFFER_ELEMENTS 20
#define BUFFER_SIZE BUFFER_ELEMENTS * sizeof(unsigned int)
#define FACTOR 100
static VKAPI_ATTR VkBool32 VKAPI_CALL DebugMessageCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT,
VkDebugUtilsMessageTypeFlagsEXT,
const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
void *
)
{
std::cerr << pCallbackData->pMessage << std::endl;
return VK_FALSE;
}
static unsigned int FindMemoryType(VkPhysicalDevice device, unsigned int type, VkMemoryPropertyFlags flags)
{
VkPhysicalDeviceMemoryProperties properties;
vkGetPhysicalDeviceMemoryProperties(device, &properties);
for (unsigned int i = 0; i < properties.memoryTypeCount; i++)
{
if (type & (1 << i) && (properties.memoryTypes[i].propertyFlags & flags) == flags)
return i;
}
return -1;
}
std::vector<char> ReadSpv(const char *filename)
{
std::fstream file(filename, std::ios::in | std::ios::binary | std::ios::ate);
if (file.fail())
std::cout << "No" << std::endl;
file.seekg(0, file.end);
int len = file.tellg();
file.seekg(0, file.beg);
std::vector<char> buf(len);
file.read(buf.data(), len);
return buf;
}
static std::string ArrayToString(unsigned int *arr, size_t size)
{
std::stringstream s;
s << '[';
for (int i = 0; i < size; i++)
{
if (i < size - 1)
s << arr[i] << ", ";
else
s << arr[i] << ']' << std::endl;
}
return s.str();
}
class VulkanDummy
{
private:
VkInstance m_instance;
VkDebugUtilsMessengerEXT m_messenger;
VkPhysicalDevice m_physicalDevice;
VkDevice m_device;
unsigned int m_familyIndex;
VkQueue m_computeQueue;
VkCommandPool m_commandPool;
VkCommandBuffer m_commandBuffer;
VkBuffer m_hostBuffer;
VkDeviceMemory m_hostMemory;
VkBuffer m_deviceBuffer;
VkDeviceMemory m_deviceMemory;
VkDescriptorPool m_descriptorPool;
VkDescriptorSetLayout m_descriptorSetLayout;
VkDescriptorSet m_descriptorSet;
VkShaderModule m_shader;
VkPipelineCache m_pipelineCache;
VkPipelineLayout m_pipelineLayout;
VkPipeline m_pipeline;
VkFence m_waitFence;
unsigned int m_inputArray[BUFFER_ELEMENTS]{};
unsigned int m_outputArray[BUFFER_ELEMENTS]{};
public:
void Run()
{
CreateInstance();
CreateValidationLayers();
CreateDevice();
CreateCommandBuffers();
CreateInputs();
CreateDescriptorSets();
CreatePipeline();
DoWork();
OutputData();
Cleanup();
}
void CreateInstance()
{
// Initialize volk loader
volkInitialize();
// Application information
VkApplicationInfo appI = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pApplicationName = "VulkanDummy",
.applicationVersion = VK_MAKE_VERSION(0, 0, 0),
.pEngineName = "None",
.engineVersion = VK_MAKE_VERSION(0, 0, 0),
.apiVersion = VK_API_VERSION_1_2,
};
// Instance layers
std::vector<const char *> layers = {
"VK_LAYER_KHRONOS_validation",
};
std::vector<const char *> extensions = {
VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
};
// Create instance
VkInstanceCreateInfo instanceCI = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &appI,
.enabledLayerCount = static_cast<uint32_t>(layers.size()),
.ppEnabledLayerNames = layers.data(),
.enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
.ppEnabledExtensionNames = extensions.data(),
};
vkCreateInstance(&instanceCI, nullptr, &m_instance);
// Load instance extension functions
volkLoadInstance(m_instance);
}
void CreateValidationLayers()
{
// Create validation messenger
VkDebugUtilsMessengerCreateInfoEXT messengerCI = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
.pfnUserCallback = DebugMessageCallback,
};
vkCreateDebugUtilsMessengerEXT(m_instance, &messengerCI, nullptr, &m_messenger);
}
void CreateDevice()
{
// Pick physical device
std::vector<VkPhysicalDevice> devices;
unsigned int deviceCount;
vkEnumeratePhysicalDevices(m_instance, &deviceCount, nullptr);
devices.resize(deviceCount);
vkEnumeratePhysicalDevices(m_instance, &deviceCount, devices.data());
// Pick the first physical device
m_physicalDevice = devices.front();
// Find queue families
std::vector<VkQueueFamilyProperties> families;
unsigned int familyCount;
vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &familyCount, nullptr);
families.resize(familyCount);
vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &familyCount, families.data());
// Request one compute queue
for (unsigned int i = 0; i < familyCount; i++)
{
if (families[i].queueFlags & VK_QUEUE_COMPUTE_BIT)
{
m_familyIndex = i;
break;
}
}
// Create queue definition
const float priorities[] = {0.0f};
VkDeviceQueueCreateInfo queueCI = {
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = m_familyIndex,
.queueCount = 1,
.pQueuePriorities = priorities,
};
// Device extensions
std::vector<const char *> extensions;
// Create device
VkDeviceCreateInfo deviceCI = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &queueCI,
.enabledExtensionCount = static_cast<uint32_t>(extensions.size()),
.ppEnabledExtensionNames = extensions.data(),
};
vkCreateDevice(m_physicalDevice, &deviceCI, nullptr, &m_device);
// Load device extension functions
volkLoadDevice(m_device);
// Fetch the requested queue
vkGetDeviceQueue(m_device, queueCI.queueFamilyIndex, 0, &m_computeQueue);
}
void CreateCommandBuffers()
{
// Create command pool
VkCommandPoolCreateInfo commandPoolCI = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = m_familyIndex,
};
vkCreateCommandPool(m_device, &commandPoolCI, nullptr, &m_commandPool);
// Allocate command buffers
VkCommandBufferAllocateInfo commandBufferAI = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = m_commandPool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
vkAllocateCommandBuffers(m_device, &commandBufferAI, &m_commandBuffer);
}
void CreateInputs()
{
// Fill input
for (int i = 0; i < BUFFER_ELEMENTS; i++)
{
m_inputArray[i] = i;
}
// Create host buffer
VkBufferCreateInfo hostBufferCI = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = BUFFER_SIZE,
.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 1,
.pQueueFamilyIndices = &m_familyIndex,
};
vkCreateBuffer(m_device, &hostBufferCI, nullptr, &m_hostBuffer);
// Allocate host buffer
VkMemoryRequirements requirements;
vkGetBufferMemoryRequirements(m_device, m_hostBuffer, &requirements);
VkMemoryAllocateInfo allocateI = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = requirements.size,
.memoryTypeIndex = FindMemoryType(m_physicalDevice, requirements.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT),
};
vkAllocateMemory(m_device, &allocateI, nullptr, &m_hostMemory);
void *mapped;
vkMapMemory(m_device, m_hostMemory, 0, BUFFER_SIZE, 0, &mapped);
memcpy(mapped, m_inputArray, BUFFER_SIZE);
vkUnmapMemory(m_device, m_hostMemory);
vkBindBufferMemory(m_device, m_hostBuffer, m_hostMemory, 0);
// Create device buffer
VkBufferCreateInfo deviceBufferCI = {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = BUFFER_SIZE,
.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE,
.queueFamilyIndexCount = 1,
.pQueueFamilyIndices = &m_familyIndex,
};
vkCreateBuffer(m_device, &deviceBufferCI, nullptr, &m_deviceBuffer);
// Allocate device buffer
vkGetBufferMemoryRequirements(m_device, m_deviceBuffer, &requirements);
allocateI = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = requirements.size,
.memoryTypeIndex = FindMemoryType(m_physicalDevice, requirements.memoryTypeBits,
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT),
};
vkAllocateMemory(m_device, &allocateI, nullptr, &m_deviceMemory);
vkBindBufferMemory(m_device, m_deviceBuffer, m_deviceMemory, 0);
// Copy host buffer content to device buffer
VkBufferCopy copy = {
.size = BUFFER_SIZE,
};
VkCommandBufferBeginInfo commandBufferBI = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
.pInheritanceInfo = nullptr,
};
vkBeginCommandBuffer(m_commandBuffer, &commandBufferBI);
vkCmdCopyBuffer(m_commandBuffer, m_hostBuffer, m_deviceBuffer, 1, &copy);
vkEndCommandBuffer(m_commandBuffer);
VkFenceCreateInfo fenceCI = {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
};
vkCreateFence(m_device, &fenceCI, nullptr, &m_waitFence);
VkSubmitInfo submitI = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.commandBufferCount = 1,
.pCommandBuffers = &m_commandBuffer,
};
vkQueueSubmit(m_computeQueue, 1, &submitI, m_waitFence);
vkWaitForFences(m_device, 1, &m_waitFence, true, UINT64_MAX);
}
void CreateDescriptorSets()
{
// Define descriptor layout
// Storage buffer binding
VkDescriptorSetLayoutBinding binding = {
.binding = 5,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
};
VkDescriptorSetLayoutCreateInfo layoutCI = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = 1,
.pBindings = &binding,
};
vkCreateDescriptorSetLayout(m_device, &layoutCI, nullptr, &m_descriptorSetLayout);
// Create descriptor pool and sets
VkDescriptorPoolSize size = {
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
};
VkDescriptorPoolCreateInfo poolCI = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.maxSets = 10,
.poolSizeCount = 1,
.pPoolSizes = &size,
};
vkCreateDescriptorPool(m_device, &poolCI, nullptr, &m_descriptorPool);
VkDescriptorSetAllocateInfo allocateI = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.descriptorPool = m_descriptorPool,
.descriptorSetCount = 1,
.pSetLayouts = &m_descriptorSetLayout,
};
vkAllocateDescriptorSets(m_device, &allocateI, &m_descriptorSet);
// Write to the descriptor set
VkDescriptorBufferInfo bufferI = {
.buffer = m_deviceBuffer,
.offset = 0,
.range = VK_WHOLE_SIZE,
};
VkWriteDescriptorSet write = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = m_descriptorSet,
.dstBinding = 5,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.pBufferInfo = &bufferI,
};
vkUpdateDescriptorSets(m_device, 1, &write, 0, nullptr);
// Create pipeline cache
VkPipelineCacheCreateInfo pipelineCacheCI = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
};
vkCreatePipelineCache(m_device, &pipelineCacheCI, nullptr, &m_pipelineCache);
}
void CreatePipeline()
{
// Load shader
std::vector<char> code = ReadSpv("shaders/inc.comp.spv");
VkShaderModuleCreateInfo shaderCI = {
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = code.size(),
.pCode = reinterpret_cast<const uint32_t *>(code.data()),
};
vkCreateShaderModule(m_device, &shaderCI, nullptr, &m_shader);
VkPipelineShaderStageCreateInfo stageCI = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_COMPUTE_BIT,
.module = m_shader,
.pName = "main",
};
// Declare push constants
VkPushConstantRange range = {
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.size = sizeof(unsigned int),
};
// Create pipeline layout
VkPipelineLayoutCreateInfo layoutCI = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 1,
.pSetLayouts = &m_descriptorSetLayout,
.pushConstantRangeCount = 1,
.pPushConstantRanges = &range,
};
vkCreatePipelineLayout(m_device, &layoutCI, nullptr, &m_pipelineLayout);
// Create pipeline layout
VkComputePipelineCreateInfo computePipelineCI = {
.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
.stage = stageCI,
.layout = m_pipelineLayout,
};
vkCreateComputePipelines(m_device, m_pipelineCache, 1, &computePipelineCI, nullptr, &m_pipeline);
}
void DoWork()
{
unsigned int fac = FACTOR;
VkCommandBufferBeginInfo commandBufferBI = {
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
};
vkBeginCommandBuffer(m_commandBuffer, &commandBufferBI);
VkBufferMemoryBarrier barrier = {
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = m_deviceBuffer,
};
vkCmdPipelineBarrier(m_commandBuffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0,
0, nullptr,
1, &barrier,
0, nullptr);
vkCmdBindPipeline(m_commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, m_pipeline);
vkCmdBindDescriptorSets(m_commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, m_pipelineLayout, 0, 1,
&m_descriptorSet, 0,
nullptr);
vkCmdPushConstants(m_commandBuffer, m_pipelineLayout, VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(unsigned int), &fac);
vkCmdDispatch(m_commandBuffer, BUFFER_ELEMENTS, 1, 1);
barrier = {
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = m_deviceBuffer,
};
vkCmdPipelineBarrier(m_commandBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
0, nullptr,
1, &barrier,
0, nullptr);
VkBufferCopy copy = {
.size = BUFFER_SIZE,
};
vkCmdCopyBuffer(m_commandBuffer, m_deviceBuffer, m_hostBuffer, 1, &copy);
barrier = {
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_HOST_READ_BIT,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.buffer = m_hostBuffer,
};
vkCmdPipelineBarrier(m_commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0,
0, nullptr,
1, &barrier,
0, nullptr);
vkEndCommandBuffer(m_commandBuffer);
// Submit work
vkResetFences(m_device, 1, &m_waitFence);
VkPipelineStageFlags waitFlags = VK_PIPELINE_STAGE_TRANSFER_BIT;
VkSubmitInfo submitI = {
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pWaitDstStageMask = &waitFlags,
.commandBufferCount = 1,
.pCommandBuffers = &m_commandBuffer,
};
vkQueueSubmit(m_computeQueue, 1, &submitI, m_waitFence);
vkWaitForFences(m_device, 1, &m_waitFence, true, UINT64_MAX);
}
void OutputData()
{
vkQueueWaitIdle(m_computeQueue);
void *mapped;
vkMapMemory(m_device, m_hostMemory, 0, BUFFER_SIZE, 0, &mapped);
memcpy(m_outputArray, mapped, BUFFER_SIZE);
vkUnmapMemory(m_device, m_hostMemory);
std::cout << "Input: " << ArrayToString(m_inputArray, BUFFER_ELEMENTS);
std::cout << "Output: " << ArrayToString(m_outputArray, BUFFER_ELEMENTS);
}
void Cleanup()
{
vkDestroyFence(m_device, m_waitFence, nullptr);
vkDestroyPipeline(m_device, m_pipeline, nullptr);
vkDestroyPipelineLayout(m_device, m_pipelineLayout, nullptr);
vkDestroyPipelineCache(m_device, m_pipelineCache, nullptr);
vkDestroyShaderModule(m_device, m_shader, nullptr);
vkDestroyDescriptorPool(m_device, m_descriptorPool, nullptr);
vkDestroyDescriptorSetLayout(m_device, m_descriptorSetLayout, nullptr);
vkFreeMemory(m_device, m_deviceMemory, nullptr);
vkDestroyBuffer(m_device, m_deviceBuffer, nullptr);
vkFreeMemory(m_device, m_hostMemory, nullptr);
vkDestroyBuffer(m_device, m_hostBuffer, nullptr);
vkFreeCommandBuffers(m_device, m_commandPool, 1, &m_commandBuffer);
vkDestroyCommandPool(m_device, m_commandPool, nullptr);
vkDestroyDevice(m_device, nullptr);
vkDestroyDebugUtilsMessengerEXT(m_instance, m_messenger, nullptr);
vkDestroyInstance(m_instance, nullptr);
}
};
int main()
{
VulkanDummy dummy;
dummy.Run();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment