Skip to content

Instantly share code, notes, and snippets.

@rdeioris
Created June 12, 2018 08:09
Show Gist options
  • Select an option

  • Save rdeioris/a39baeeca23c3dc17eac0a0311d6a736 to your computer and use it in GitHub Desktop.

Select an option

Save rdeioris/a39baeeca23c3dc17eac0a0311d6a736 to your computer and use it in GitHub Desktop.
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
#include <vector>
#include <iostream>
#include <exception>
class VulkanApp
{
public:
VulkanApp(uint32_t width, uint32_t height)
{
glfwInit();
// disable OpenGL features and block window resizing (as rebuilding the viewport is a new command)
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
// create the os window
windowWidth = width;
windowHeight = height;
window = glfwCreateWindow(windowWidth, windowHeight, "Hello Vulkan", nullptr, nullptr);
if (!window)
{
throw std::runtime_error("unable to create window");
}
// could be required for drivers (ICD) optimizations
VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "First Vulkan Test";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "Broken Engine";
appInfo.apiVersion = VK_API_VERSION_1_0;
VkInstanceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
// ask glfw which vulkan extensions are required
uint32_t extensionsCount = 0;
const char** extensions = glfwGetRequiredInstanceExtensions(&extensionsCount);
for (uint32_t i = 0; i < extensionsCount; i++)
{
std::cout << extensions[i] << std::endl;
}
// set required extensions
createInfo.ppEnabledExtensionNames = extensions;
createInfo.enabledExtensionCount = extensionsCount;
// finally create the instance
VkResult result = vkCreateInstance(&createInfo, nullptr, &instance);
if (result != VK_SUCCESS)
{
throw std::runtime_error("unable to create Vulkan instance");
}
std::cout << "Vulkan initialized" << std::endl;
}
VkPhysicalDevice GetBestDevice()
{
// get the number of GPUs
uint32_t numberOfGpus = 0;
VkResult result = vkEnumeratePhysicalDevices(instance, &numberOfGpus, nullptr);
if (result != VK_SUCCESS)
{
throw std::runtime_error("unable to get the number of physical devices");
}
std::vector<VkPhysicalDevice> physicalDevices(numberOfGpus);
result = vkEnumeratePhysicalDevices(instance, &numberOfGpus, physicalDevices.data());
if (result != VK_SUCCESS)
{
throw std::runtime_error("unable to get the list of physical devices");
}
// get infos about each GPU
for (uint32_t i = 0; i < numberOfGpus; i++)
{
VkPhysicalDeviceProperties physicalProperties;
vkGetPhysicalDeviceProperties(physicalDevices[i], &physicalProperties);
std::cout << i << " " << physicalProperties.deviceName << " " << physicalProperties.deviceType << std::endl;
uint32_t numberOfFamilies = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevices[i], &numberOfFamilies, nullptr);
std::vector<VkQueueFamilyProperties> familyProperties(numberOfFamilies);
vkGetPhysicalDeviceQueueFamilyProperties(physicalDevices[i], &numberOfFamilies, familyProperties.data());
for (uint32_t j = 0; j < numberOfFamilies; j++)
{
std::cout << "\t" << j << " " << familyProperties[j].queueCount << " " << std::hex << familyProperties[j].queueFlags << std::dec << std::endl;
}
}
return physicalDevices[0];
}
VkDevice CreateLogicalDevice(VkPhysicalDevice device)
{
VkDeviceQueueCreateInfo queueCreateInfo = {};
float priority = 1.0;
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = 0;
// queueCount specifies the number of queues to allocate from the specific family
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &priority;
// we want a logical device with swapchain support
const char *deviceExtensions[] = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME
};
VkDeviceCreateInfo deviceCreateInfo = {};
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo;
deviceCreateInfo.queueCreateInfoCount = 1;
deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions;
deviceCreateInfo.enabledExtensionCount = 1;
VkDevice logicalDevice;
// finally create the logical device
VkResult result = vkCreateDevice(device, &deviceCreateInfo, nullptr, &logicalDevice);
if (result != VK_SUCCESS)
{
throw std::runtime_error("unable to create logical device");
}
return logicalDevice;
}
VkSwapchainKHR CreateSwapChain(VkPhysicalDevice device, VkDevice logicalDevice)
{
// ask glfw for a window surface to draw onto
VkSurfaceKHR surface;
VkResult result = glfwCreateWindowSurface(instance, window, nullptr, &surface);
if (result != VK_SUCCESS)
{
throw std::runtime_error("unable to create surface for swap chain");
}
// get the list of surface support formats
uint32_t surfaceFormatsCount = 0;
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &surfaceFormatsCount, nullptr);
std::vector<VkSurfaceFormatKHR> surfaceFormats(surfaceFormatsCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &surfaceFormatsCount, surfaceFormats.data());
for (uint32_t i = 0; i < surfaceFormatsCount; i++)
{
std::cout << "format: " << surfaceFormats[i].format << std::endl;
}
// get surface capabilities
VkSurfaceCapabilitiesKHR surfaceCapabilities;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &surfaceCapabilities);
VkSwapchainKHR swapChain;
// create the swapchain
VkSwapchainCreateInfoKHR swapInfo = {};
swapInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapInfo.surface = surface;
swapInfo.minImageCount = 2;
swapInfo.imageFormat = VK_FORMAT_B8G8R8A8_UNORM;
swapInfo.imageColorSpace = surfaceFormats[0].colorSpace;
swapInfo.imageExtent.width = windowWidth;
swapInfo.imageExtent.height = windowHeight;
swapInfo.imageArrayLayers = 1;
swapInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
swapInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
swapInfo.oldSwapchain = VK_NULL_HANDLE;
swapInfo.clipped = VK_TRUE;
swapInfo.preTransform = surfaceCapabilities.currentTransform;
swapInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
swapInfo.presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
// finally create the swapchain
result = vkCreateSwapchainKHR(logicalDevice, &swapInfo, nullptr, &swapChain);
if (result != VK_SUCCESS)
{
throw std::runtime_error("unable to create swap chain");
}
return swapChain;
}
VkDeviceMemory GpuMalloc(VkPhysicalDevice device, VkDevice logicalDevice, uint32_t size, uint32_t flags)
{
VkPhysicalDeviceMemoryProperties memProps;
vkGetPhysicalDeviceMemoryProperties(device, &memProps);
uint32_t bestIndex = 0;
for (int i = 0; i < memProps.memoryTypeCount; i++)
{
//std::cout << "memProp " << i << " " << memProps.memoryTypes[i].propertyFlags << std::endl;
if (memProps.memoryTypes[i].propertyFlags & flags)
{
bestIndex = i;
break;
}
}
VkDeviceMemory memory;
VkMemoryAllocateInfo memAllocateInfo = {};
memAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
memAllocateInfo.memoryTypeIndex = bestIndex;
memAllocateInfo.allocationSize = size;
VkResult result = vkAllocateMemory(logicalDevice, &memAllocateInfo, nullptr, &memory);
if (result != VK_SUCCESS)
{
throw std::runtime_error("unable to allocate memory for image");
}
return memory;
}
VkImage CreateImage(VkDevice logicalDevice, uint32_t width, uint32_t height)
{
VkImageCreateInfo imageCreateInfo = {};
imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageCreateInfo.extent.width = 512;
imageCreateInfo.extent.height = 512;
imageCreateInfo.extent.depth = 1;
imageCreateInfo.format = VK_FORMAT_R8G8B8A8_UINT;
imageCreateInfo.imageType = VK_IMAGE_TYPE_2D;
imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageCreateInfo.mipLevels = 1;
imageCreateInfo.arrayLayers = 1;
imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageCreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
imageCreateInfo.tiling = VK_IMAGE_TILING_LINEAR;
VkImage image;
VkResult result = vkCreateImage(logicalDevice, &imageCreateInfo, nullptr, &image);
if (result != VK_SUCCESS)
{
throw std::runtime_error("unable to create image");
}
return image;
}
bool IsOpened()
{
return !glfwWindowShouldClose(window);
}
private:
GLFWwindow * window;
// an instance is the connection between the app and the vulkan system
VkInstance instance;
uint32_t windowWidth;
uint32_t windowHeight;
};
int main(int argc, char **argv)
{
VulkanApp app(800, 600);
VkPhysicalDevice physicalDevice = app.GetBestDevice();
// virtual representation of a physical device paired with a queue
VkDevice logicalDevice = app.CreateLogicalDevice(physicalDevice);
// get a ref to the specified queue family: 0 queue: 0
VkQueue graphicsQueue;
vkGetDeviceQueue(logicalDevice, 0, 0, &graphicsQueue);
VkSwapchainKHR swapChain = app.CreateSwapChain(physicalDevice, logicalDevice);
// get the array of VkImage's
uint32_t numberOfImages = 0;
vkGetSwapchainImagesKHR(logicalDevice, swapChain, &numberOfImages, nullptr);
std::vector<VkImage> swapChainImages(numberOfImages);
vkGetSwapchainImagesKHR(logicalDevice, swapChain, &numberOfImages, swapChainImages.data());
std::cout << "number of swapchain images: " << numberOfImages << std::endl;
// a command pool is required for dynamic allocation of commands in command buffers
VkCommandPool commandPool;
VkCommandPoolCreateInfo poolCreateInfo = {};
poolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolCreateInfo.queueFamilyIndex = 0;
VkResult result = vkCreateCommandPool(logicalDevice, &poolCreateInfo, nullptr, &commandPool);
if (result != VK_SUCCESS)
{
throw std::runtime_error("unable to create command pool");
}
// one for image in the swapchain
std::vector<VkCommandBuffer> commandBuffers(2);
VkCommandBufferAllocateInfo allocateInfo = {};
allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocateInfo.commandPool = commandPool;
allocateInfo.commandBufferCount = 2;
allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
result = vkAllocateCommandBuffers(logicalDevice, &allocateInfo, commandBuffers.data());
if (result != VK_SUCCESS)
{
throw std::runtime_error("unable to allocate command buffers");
}
// create an image 256*256
VkImage imageToBlit = app.CreateImage(logicalDevice, 256, 256);
// ask the GPU how much memory (and its type) is required for the specific image
VkMemoryRequirements memReq;
vkGetImageMemoryRequirements(logicalDevice, imageToBlit, &memReq);
VkDeviceMemory imageMemory = app.GpuMalloc(physicalDevice, logicalDevice, memReq.size, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
// map the memory to the image
result = vkBindImageMemory(logicalDevice, imageToBlit, imageMemory, 0);
if (result != VK_SUCCESS)
{
throw std::runtime_error("unable to bind memory to image");
}
// memory map (MMU-based)
unsigned char *data;
result = vkMapMemory(logicalDevice, imageMemory, 0, memReq.size, 0, (void **)&data);
if (result != VK_SUCCESS)
{
throw std::runtime_error("unable to map image memory");
}
for (int i = 0; i < memReq.size; i += 4)
{
data[i] = 255;
data[i + 1] = rand() % 255;
data[i + 2] = 0;
data[i + 3] = 255;
}
// unmap memory
vkUnmapMemory(logicalDevice, imageMemory);
float x = 0;
float y = 0;
while (app.IsOpened())
{
glfwPollEvents();
uint32_t imageIndex;
// get the first available image in the swapchain
vkAcquireNextImageKHR(logicalDevice, swapChain, std::numeric_limits<uint64_t>::max(), VK_NULL_HANDLE, VK_NULL_HANDLE, &imageIndex);
//std::cout << imageIndex << std::endl;
x += 0.05;
y += 0.05;
vkResetCommandBuffer(commandBuffers[imageIndex], 0);
// rebuild/re-record command buffer
// build the command sequence (once for each command buffer/swapchain image)
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
// start recording commands
result = vkBeginCommandBuffer(commandBuffers[imageIndex], &beginInfo);
if (result != VK_SUCCESS)
{
throw std::runtime_error("unable to start recording");
}
// here you put your commands
// ...
VkClearColorValue clearColor = { 0, 1.0, 1.0, 1.0 };
VkImageSubresourceRange range = {};
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
range.baseMipLevel = 0;
range.levelCount = 1;
range.baseArrayLayer = 0;
range.layerCount = 1;
vkCmdClearColorImage(commandBuffers[imageIndex], swapChainImages[imageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &clearColor, 1, &range);
VkImageCopy imageCopy = {};
imageCopy.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageCopy.srcSubresource.mipLevel = 0;
imageCopy.srcSubresource.baseArrayLayer = 0;
imageCopy.srcSubresource.layerCount = 1;
imageCopy.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
imageCopy.dstSubresource.mipLevel = 0;
imageCopy.dstSubresource.baseArrayLayer = 0;
imageCopy.dstSubresource.layerCount = 1;
imageCopy.srcOffset.x = 0;
imageCopy.srcOffset.y = 0;
imageCopy.srcOffset.z = 0;
imageCopy.dstOffset.x = (int32_t)x;
imageCopy.dstOffset.y = (int32_t)y;
imageCopy.dstOffset.z = 0;
imageCopy.extent.width = 256;
imageCopy.extent.height = 256;
imageCopy.extent.depth = 1;
vkCmdCopyImage(commandBuffers[imageIndex], imageToBlit, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, swapChainImages[imageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageCopy);
// end registration
result = vkEndCommandBuffer(commandBuffers[imageIndex]);
if (result != VK_SUCCESS)
{
throw std::runtime_error("unable to stop recording");
}
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
//execute the previously registered command buffer
result = vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
if (result != VK_SUCCESS)
{
throw std::runtime_error("unable to submit command buffer");
}
VkPresentInfoKHR presentInfo = {};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = &swapChain;
presentInfo.pImageIndices = &imageIndex;
// show the swapchain image into the surface
vkQueuePresentKHR(graphicsQueue, &presentInfo);
}
// here you should cleanup...
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment