Created
June 12, 2018 08:09
-
-
Save rdeioris/a39baeeca23c3dc17eac0a0311d6a736 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #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