A direct conversion of this vulkan example to C
Last active
August 16, 2020 18:08
-
-
Save porky11/29b55602b1bc1f57a8cde9034bd65caf to your computer and use it in GitHub Desktop.
This file contains 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
#include <assert.h> | |
#include <string.h> | |
#define GLFW_INCLUDE_VULKAN | |
#include <GLFW/glfw3.h> | |
#include <stdbool.h> | |
#include <stdio.h> | |
#define VKCHECK(x) if ((x) != VK_SUCCESS) fprintf(stderr, "Failure while executing Vulkan command at line %d\n", __LINE__) | |
static bool getMemoryTypeFromProperties(VkPhysicalDeviceMemoryProperties memProps, uint32_t typeBits, VkFlags requirementsMask, uint32_t* typeIndex) | |
{ | |
for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i) { | |
if ((typeBits & 1) == 1) { | |
if ((memProps.memoryTypes[i].propertyFlags & requirementsMask) == requirementsMask) { | |
*typeIndex = i; | |
return true; | |
} | |
} | |
typeBits >>= 1; | |
} | |
return false; | |
} | |
#define VALIDATION_LAYER_COUNT (uint32_t)1 | |
static const char* instanceValidationLayerNames[VALIDATION_LAYER_COUNT] = { "VK_LAYER_LUNARG_standard_validation" }; | |
/* | |
#define VALIDATION_LAYER_COUNT (uint32_t)8 | |
static const char* instanceValidationLayerNames[8] = { | |
"VK_LAYER_GOOGLE_threading", "VK_LAYER_LUNARG_parameter_validation", | |
"VK_LAYER_LUNARG_device_limits", "VK_LAYER_LUNARG_object_tracker", | |
"VK_LAYER_LUNARG_image", "VK_LAYER_LUNARG_core_validation", | |
"VK_LAYER_LUNARG_swapchain", "VK_LAYER_GOOGLE_unique_objects" | |
};*/ | |
static void setImageLayout(VkCommandBuffer* cmdBuf, VkDevice device, VkCommandPool cmdPool, VkImage image, VkImageAspectFlags aspectMask, VkImageLayout oldImageLayout, VkImageLayout newImageLayout, VkAccessFlagBits srcAccessMask) | |
{ | |
// allocate a command buffer if we don't already have one | |
if (*cmdBuf == VK_NULL_HANDLE) { | |
VkCommandBufferAllocateInfo cmdBufAllocInfo = {}; | |
cmdBufAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; | |
cmdBufAllocInfo.pNext = NULL; | |
cmdBufAllocInfo.commandPool = cmdPool; | |
cmdBufAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; | |
cmdBufAllocInfo.commandBufferCount = 1; | |
VKCHECK(vkAllocateCommandBuffers(device, &cmdBufAllocInfo, cmdBuf)); | |
VkCommandBufferInheritanceInfo cmdBufInhInfo = {}; | |
cmdBufInhInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; | |
cmdBufInhInfo.pNext = NULL; | |
cmdBufInhInfo.renderPass = VK_NULL_HANDLE; | |
cmdBufInhInfo.subpass = 0; | |
cmdBufInhInfo.framebuffer = VK_NULL_HANDLE; | |
cmdBufInhInfo.occlusionQueryEnable = VK_FALSE; | |
cmdBufInhInfo.queryFlags = 0; | |
cmdBufInhInfo.pipelineStatistics = 0; | |
VkCommandBufferBeginInfo cmdBufBeginInfo = {}; | |
cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; | |
cmdBufBeginInfo.pNext = NULL; | |
cmdBufBeginInfo.flags = 0; | |
cmdBufBeginInfo.pInheritanceInfo = &cmdBufInhInfo; | |
VKCHECK(vkBeginCommandBuffer(*cmdBuf, &cmdBufBeginInfo)); | |
} | |
VkImageMemoryBarrier imageMemBarrier = {}; | |
imageMemBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; | |
imageMemBarrier.pNext = NULL; | |
imageMemBarrier.srcAccessMask = srcAccessMask; | |
imageMemBarrier.dstAccessMask = 0; | |
imageMemBarrier.oldLayout = oldImageLayout; | |
imageMemBarrier.newLayout = newImageLayout; | |
imageMemBarrier.image = image; | |
imageMemBarrier.subresourceRange = (VkImageSubresourceRange){ aspectMask, 0, 1, 0, 1 }; | |
if (newImageLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { | |
imageMemBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; | |
} else if (newImageLayout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { | |
imageMemBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; | |
} else if (newImageLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { | |
imageMemBarrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; | |
} else if (newImageLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { | |
imageMemBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; | |
} | |
VkPipelineStageFlags srcStages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; | |
VkPipelineStageFlags destStages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; | |
vkCmdPipelineBarrier(*cmdBuf, srcStages, destStages, 0, 0, NULL, 0, NULL, 1, &imageMemBarrier); | |
} | |
static bool checkInstanceLayers(uint32_t checkCount, const char* const* checkNames, uint32_t layerCount, VkLayerProperties* layers) | |
{ | |
for (uint32_t i = 0; i < checkCount; ++i) { | |
bool found = false; | |
for (uint32_t j = 0; j < layerCount; ++j) { | |
if (strcmp(checkNames[i], layers[j].layerName) == 0) { | |
found = true; | |
break; | |
} | |
} | |
if (!found) { | |
fprintf(stderr, "Cannot find instance layer %s\n", checkNames[i]); | |
return false; | |
} | |
} | |
return true; | |
} | |
static const bool validate = false; | |
VkBool32 debugCallback(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, size_t location, int32_t msgCode, const char* pLayerPrefix, const char* pMsg, void* pUserData) { | |
if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) | |
{ | |
fprintf(stderr, "ERROR: [%s] Code %d : %s\n", pLayerPrefix, msgCode, pMsg); | |
} else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) | |
{ | |
fprintf(stderr, "WARNING: [%s] Code %d : %s\n", pLayerPrefix, msgCode, pMsg); | |
} else { | |
fprintf(stderr, "INFO: [%s] Code %d : %s\n", pLayerPrefix, msgCode, pMsg); | |
} | |
return VK_FALSE; | |
} | |
int main() | |
{ | |
glfwInit(); | |
assert(glfwVulkanSupported()); | |
// enable validation if wanted | |
bool validationFound = false; | |
if (validate) { | |
uint32_t propCount; | |
VKCHECK(vkEnumerateInstanceLayerProperties(&propCount, NULL)); | |
VkLayerProperties instanceLayerProps[propCount]; | |
VKCHECK(vkEnumerateInstanceLayerProperties(&propCount, instanceLayerProps)); | |
validationFound = checkInstanceLayers(VALIDATION_LAYER_COUNT, instanceValidationLayerNames, propCount, instanceLayerProps); | |
} | |
if (validate && !validationFound) { | |
fprintf(stderr, "Validation was requested, but not all required validation layers were found.\n"); | |
return 1; | |
} | |
PFN_vkCreateInstance pfnCreateInstance = (PFN_vkCreateInstance)glfwGetInstanceProcAddress(VK_NULL_HANDLE, "vkCreateInstance"); | |
PFN_vkDestroyInstance pfnDestroyInstance = (PFN_vkDestroyInstance)glfwGetInstanceProcAddress(VK_NULL_HANDLE, "vkDestroyInstance"); | |
unsigned int instanceExtCount; | |
const char** reqInstanceExts = glfwGetRequiredInstanceExtensions(&instanceExtCount); | |
if (reqInstanceExts) { | |
printf("Required instance extensions:\n"); | |
for (unsigned int i = 0; i < instanceExtCount; ++i) { | |
puts(reqInstanceExts[i]); | |
} | |
} | |
const char* instanceExts[8]; | |
memcpy(instanceExts, reqInstanceExts, sizeof(const char*) * instanceExtCount); | |
if (validate) { | |
instanceExts[instanceExtCount++] = VK_EXT_DEBUG_REPORT_EXTENSION_NAME; // we just assume it's there | |
} | |
VkApplicationInfo appInfo = {}; | |
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; | |
appInfo.pNext = NULL; | |
appInfo.pApplicationName = "glfwvktest"; | |
appInfo.applicationVersion = 1; | |
appInfo.pEngineName = "glfwvktest"; | |
appInfo.engineVersion = 1; | |
appInfo.apiVersion = VK_API_VERSION_1_0; | |
VkInstanceCreateInfo instanceCreateInfo = {}; | |
instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; | |
instanceCreateInfo.pNext = NULL; | |
instanceCreateInfo.flags = 0; | |
instanceCreateInfo.pApplicationInfo = &appInfo; | |
if (validate) { | |
instanceCreateInfo.enabledLayerCount = VALIDATION_LAYER_COUNT; | |
instanceCreateInfo.ppEnabledLayerNames = instanceValidationLayerNames; | |
} | |
instanceCreateInfo.enabledExtensionCount = instanceExtCount; | |
instanceCreateInfo.ppEnabledExtensionNames = instanceExts; | |
VkDebugReportCallbackCreateInfoEXT dbgCreateInfo = {}; | |
dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; | |
dbgCreateInfo.pNext = NULL; | |
dbgCreateInfo.pfnCallback = debugCallback; | |
dbgCreateInfo.pUserData = NULL; | |
dbgCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; | |
instanceCreateInfo.pNext = &dbgCreateInfo; | |
VkInstance instance; | |
VKCHECK(pfnCreateInstance(&instanceCreateInfo, NULL, &instance)); | |
// set the debug report callback | |
PFN_vkCreateDebugReportCallbackEXT pfnCreateDebugReportCallbackEXT = NULL; | |
PFN_vkDestroyDebugReportCallbackEXT pfnDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)glfwGetInstanceProcAddress(instance, "vkDestroyDebugReportCallbackEXT"); | |
VkDebugReportCallbackEXT debugReportCallback; | |
if (validate) { | |
pfnCreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)glfwGetInstanceProcAddress(instance, "vkCreateDebugReportCallbackEXT"); | |
pfnDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)glfwGetInstanceProcAddress(instance, "vkDestroyDebugReportCallbackEXT"); | |
VKCHECK(pfnCreateDebugReportCallbackEXT(instance, &dbgCreateInfo, NULL, &debugReportCallback)); | |
} | |
PFN_vkCreateDevice pfnCreateDevice = (PFN_vkCreateDevice)glfwGetInstanceProcAddress(instance, "vkCreateDevice"); | |
PFN_vkDestroyDevice pfnDestroyDevice = (PFN_vkDestroyDevice)glfwGetInstanceProcAddress(instance, "vkDestroyDevice"); | |
PFN_vkGetDeviceProcAddr pfnGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)glfwGetInstanceProcAddress(instance, "vkGetDeviceProcAddr"); | |
PFN_vkEnumeratePhysicalDevices pfnEnumeratePhysicalDevices = (PFN_vkEnumeratePhysicalDevices)glfwGetInstanceProcAddress(instance, "vkEnumeratePhysicalDevices"); | |
PFN_vkGetPhysicalDeviceQueueFamilyProperties pfnGetPhysicalDeviceQueueFamilyProperties = (PFN_vkGetPhysicalDeviceQueueFamilyProperties)glfwGetInstanceProcAddress(instance, "vkGetPhysicalDeviceQueueFamilyProperties"); | |
PFN_vkDestroySurfaceKHR pfnDestroySurfaceKHR = (PFN_vkDestroySurfaceKHR)glfwGetInstanceProcAddress(instance, "vkDestroySurfaceKHR"); | |
uint32_t gpuCount; | |
VKCHECK(pfnEnumeratePhysicalDevices(instance, &gpuCount, NULL)); | |
VkPhysicalDevice gpu; | |
{ | |
VkPhysicalDevice gpus[gpuCount]; | |
VKCHECK(pfnEnumeratePhysicalDevices(instance, &gpuCount, gpus)); | |
gpu = gpus[0]; | |
} | |
uint32_t deviceExtensionCount = 0; | |
VKCHECK(vkEnumerateDeviceExtensionProperties(gpu, NULL, &deviceExtensionCount, NULL)); | |
const char* enabledDeviceExtensions[4]; | |
uint32_t enabledDeviceExtensionCount = 0; | |
bool swapchainExtFound = false; | |
{ | |
VkExtensionProperties deviceExtensions[deviceExtensionCount]; | |
VKCHECK(vkEnumerateDeviceExtensionProperties(gpu, NULL, &deviceExtensionCount, deviceExtensions)); | |
for (uint32_t i = 0; i < deviceExtensionCount; ++i) { | |
if (strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME, deviceExtensions[i].extensionName) == 0) { | |
swapchainExtFound = true; | |
enabledDeviceExtensions[enabledDeviceExtensionCount++] = VK_KHR_SWAPCHAIN_EXTENSION_NAME; | |
} | |
} | |
} | |
if (!swapchainExtFound) { | |
assert(false && "Need the swapchain extension, but it wasn't found"); | |
} | |
uint32_t queueFamilyCount; | |
pfnGetPhysicalDeviceQueueFamilyProperties(gpu, &queueFamilyCount, NULL); | |
uint32_t suitableQueueFamilyIndex; | |
{ | |
VkQueueFamilyProperties queueFamilyProperties[queueFamilyCount]; | |
pfnGetPhysicalDeviceQueueFamilyProperties(gpu, &queueFamilyCount, queueFamilyProperties); | |
for (uint32_t i = 0; i < queueFamilyCount; ++i) { | |
if (queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { | |
if (glfwGetPhysicalDevicePresentationSupport(instance, gpu, i)) { | |
suitableQueueFamilyIndex = i; | |
break; | |
} | |
} | |
} | |
} | |
VkDeviceQueueCreateInfo queueCreateInfo = {}; | |
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; | |
queueCreateInfo.pNext = NULL; | |
queueCreateInfo.flags = 0; | |
queueCreateInfo.queueFamilyIndex = suitableQueueFamilyIndex; | |
queueCreateInfo.queueCount = 1; | |
float queuePriority = 0.0f; | |
queueCreateInfo.pQueuePriorities = &queuePriority; | |
VkDeviceCreateInfo deviceCreateInfo = {}; | |
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; | |
deviceCreateInfo.pNext = NULL; | |
deviceCreateInfo.flags = 0; | |
deviceCreateInfo.queueCreateInfoCount = 1; | |
deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; | |
if (validate) { | |
deviceCreateInfo.enabledLayerCount = VALIDATION_LAYER_COUNT; // TODO: layer checking omitted | |
deviceCreateInfo.ppEnabledLayerNames = instanceValidationLayerNames; | |
} | |
deviceCreateInfo.enabledExtensionCount = enabledDeviceExtensionCount; | |
deviceCreateInfo.ppEnabledExtensionNames = enabledDeviceExtensions; | |
deviceCreateInfo.pEnabledFeatures = NULL; | |
VkDevice device; | |
VKCHECK(pfnCreateDevice(gpu, &deviceCreateInfo, NULL, &device)); | |
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); | |
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); | |
uint32_t width = 1280; | |
uint32_t height = 720; | |
GLFWwindow* window = glfwCreateWindow(width, height, "Vulkan on GLFW", NULL, NULL); | |
VkSurfaceKHR surface; | |
glfwCreateWindowSurface(instance, window, NULL, &surface); | |
VkBool32 surfSupported = VK_FALSE; | |
VKCHECK(vkGetPhysicalDeviceSurfaceSupportKHR(gpu, suitableQueueFamilyIndex, surface, &surfSupported)); | |
assert(surfSupported == VK_TRUE); | |
VkQueue queue; | |
vkGetDeviceQueue(device, suitableQueueFamilyIndex, 0, &queue); | |
VkSurfaceCapabilitiesKHR surfCaps; | |
VKCHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(gpu, surface, &surfCaps)); | |
VkPhysicalDeviceMemoryProperties memProps; | |
vkGetPhysicalDeviceMemoryProperties(gpu, &memProps); | |
uint32_t presentModeCount; | |
VKCHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(gpu, surface, &presentModeCount, NULL)); | |
VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; | |
{ | |
VkPresentModeKHR presentModes[presentModeCount]; | |
VKCHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(gpu, surface, &presentModeCount, presentModes)); | |
for (uint32_t i = 0; i < presentModeCount; ++i) { | |
if (presentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { | |
swapchainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR; | |
break; | |
} | |
if ((swapchainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR) && | |
(presentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)) { | |
swapchainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; | |
} | |
} | |
} | |
VkExtent2D swapchainExtent = {}; | |
if (surfCaps.currentExtent.width == 0xffffffff) { | |
swapchainExtent.width = width; | |
swapchainExtent.height = height; | |
} else { | |
swapchainExtent = surfCaps.currentExtent; | |
width = surfCaps.currentExtent.width; | |
height = surfCaps.currentExtent.height; | |
} | |
VkSurfaceTransformFlagBitsKHR preTransform; | |
if (surfCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) { | |
preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; | |
} else { | |
preTransform = surfCaps.currentTransform; | |
} | |
uint32_t formatCount; | |
VKCHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &formatCount, NULL)); | |
VkFormat suitableFormat; | |
VkColorSpaceKHR suitableColorSpace; | |
{ | |
VkSurfaceFormatKHR surfFormats[formatCount]; | |
VKCHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &formatCount, surfFormats)); | |
if ((formatCount == 1) && (surfFormats[0].format == VK_FORMAT_UNDEFINED)) { | |
suitableFormat = VK_FORMAT_B8G8R8A8_UNORM; | |
} else { | |
assert(formatCount >= 1); | |
suitableFormat = surfFormats[0].format; | |
} | |
suitableColorSpace = surfFormats[0].colorSpace; | |
} | |
VkSwapchainCreateInfoKHR swapchainCreateInfo = {}; | |
swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; | |
swapchainCreateInfo.pNext = NULL; | |
swapchainCreateInfo.flags = 0; | |
swapchainCreateInfo.surface = surface; | |
swapchainCreateInfo.minImageCount = 2; | |
swapchainCreateInfo.imageFormat = suitableFormat; | |
swapchainCreateInfo.imageColorSpace = suitableColorSpace; | |
swapchainCreateInfo.imageExtent = swapchainExtent; | |
swapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; | |
swapchainCreateInfo.preTransform = preTransform; | |
swapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; | |
swapchainCreateInfo.imageArrayLayers = 1; | |
swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; | |
swapchainCreateInfo.queueFamilyIndexCount = 0; | |
swapchainCreateInfo.pQueueFamilyIndices = NULL; | |
swapchainCreateInfo.presentMode = swapchainPresentMode; | |
swapchainCreateInfo.oldSwapchain = VK_NULL_HANDLE; | |
swapchainCreateInfo.clipped = VK_TRUE; | |
PFN_vkCreateSwapchainKHR pfnCreateSwapchainKHR = (PFN_vkCreateSwapchainKHR)pfnGetDeviceProcAddr(device, "vkCreateSwapchainKHR"); | |
PFN_vkDestroySwapchainKHR pfnDestroySwapchainKHR = (PFN_vkDestroySwapchainKHR)pfnGetDeviceProcAddr(device, "vkDestroySwapchainKHR"); | |
PFN_vkGetSwapchainImagesKHR pfnGetSwapchainImagesKHR = (PFN_vkGetSwapchainImagesKHR)pfnGetDeviceProcAddr(device, "vkGetSwapchainImagesKHR"); | |
PFN_vkAcquireNextImageKHR pfnAcquireNextImageKHR = (PFN_vkAcquireNextImageKHR)pfnGetDeviceProcAddr(device, "vkAcquireNextImageKHR"); | |
PFN_vkQueuePresentKHR pfnQueuePresentKHR = (PFN_vkQueuePresentKHR)pfnGetDeviceProcAddr(device, "vkQueuePresentKHR"); | |
VkSwapchainKHR swapchain; | |
VKCHECK(pfnCreateSwapchainKHR(device, &swapchainCreateInfo, NULL, &swapchain)); | |
VkImage swapchainImages[2]; | |
uint32_t swapchainImageCount; | |
VKCHECK(pfnGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, NULL)); | |
assert(swapchainImageCount == 2); | |
VKCHECK(pfnGetSwapchainImagesKHR(device, swapchain, &swapchainImageCount, swapchainImages)); | |
uint32_t currentSwapchainImage = 0; | |
// create a command pool | |
VkCommandPoolCreateInfo cmdPoolCreateInfo = {}; | |
cmdPoolCreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; | |
cmdPoolCreateInfo.pNext = NULL; | |
cmdPoolCreateInfo.flags = 0; | |
cmdPoolCreateInfo.queueFamilyIndex = suitableQueueFamilyIndex; | |
VkCommandPool cmdPool; | |
VKCHECK(vkCreateCommandPool(device, &cmdPoolCreateInfo, NULL, &cmdPool)); | |
// create a depth buffer | |
VkFormat depthFormat = VK_FORMAT_D32_SFLOAT_S8_UINT; | |
VkImageCreateInfo dsImageCreateInfo = {}; | |
dsImageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; | |
dsImageCreateInfo.pNext = NULL; | |
dsImageCreateInfo.flags = 0; | |
dsImageCreateInfo.imageType = VK_IMAGE_TYPE_2D; | |
dsImageCreateInfo.format = depthFormat; | |
dsImageCreateInfo.extent = (VkExtent3D){ width, height, 1 }; | |
dsImageCreateInfo.mipLevels = 1; | |
dsImageCreateInfo.arrayLayers = 1; | |
dsImageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; | |
dsImageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; | |
dsImageCreateInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; | |
VkImageViewCreateInfo dsivCreateInfo = {}; | |
dsivCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; | |
dsivCreateInfo.pNext = NULL; | |
dsivCreateInfo.flags = 0; | |
dsivCreateInfo.image = VK_NULL_HANDLE; | |
dsivCreateInfo.format = depthFormat; | |
dsivCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; | |
dsivCreateInfo.subresourceRange.baseMipLevel = 0; | |
dsivCreateInfo.subresourceRange.levelCount = 1; | |
dsivCreateInfo.subresourceRange.baseArrayLayer = 0; | |
dsivCreateInfo.subresourceRange.layerCount = 1; | |
dsivCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; | |
VkMemoryRequirements dsMemReqs; | |
VkImage dsImage; | |
VKCHECK(vkCreateImage(device, &dsImageCreateInfo, NULL, &dsImage)); | |
vkGetImageMemoryRequirements(device, dsImage, &dsMemReqs); | |
VkMemoryAllocateInfo dsMemAllocInfo = {}; | |
dsMemAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; | |
dsMemAllocInfo.pNext = NULL; | |
dsMemAllocInfo.allocationSize = dsMemReqs.size; | |
dsMemAllocInfo.memoryTypeIndex = 0; | |
bool ok = getMemoryTypeFromProperties(memProps, dsMemReqs.memoryTypeBits, 0, &dsMemAllocInfo.memoryTypeIndex); | |
assert(ok); | |
VkDeviceMemory dsMemory; | |
VKCHECK(vkAllocateMemory(device, &dsMemAllocInfo, NULL, &dsMemory)); | |
VKCHECK(vkBindImageMemory(device, dsImage, dsMemory, 0)); | |
VkCommandBuffer cmdBuf = VK_NULL_HANDLE; | |
setImageLayout(&cmdBuf, device, cmdPool, dsImage, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, (VkAccessFlagBits)0); | |
VkImageView dsImageView; | |
dsivCreateInfo.image = dsImage; | |
VKCHECK(vkCreateImageView(device, &dsivCreateInfo, NULL, &dsImageView)); | |
VkImageViewCreateInfo ivCreateInfo = {}; | |
ivCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; | |
ivCreateInfo.pNext = NULL; | |
ivCreateInfo.flags = 0; | |
ivCreateInfo.format = suitableFormat; | |
ivCreateInfo.components = (VkComponentMapping){ | |
VK_COMPONENT_SWIZZLE_R, | |
VK_COMPONENT_SWIZZLE_G, | |
VK_COMPONENT_SWIZZLE_B, | |
VK_COMPONENT_SWIZZLE_A | |
}; | |
ivCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; | |
ivCreateInfo.subresourceRange.baseMipLevel = 0; | |
ivCreateInfo.subresourceRange.levelCount = 1; | |
ivCreateInfo.subresourceRange.baseArrayLayer = 0; | |
ivCreateInfo.subresourceRange.layerCount = 1; | |
ivCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; | |
VkImageView presentViews[2]; | |
for (uint32_t i = 0; i < 2; ++i) { | |
ivCreateInfo.image = swapchainImages[i]; | |
VKCHECK(vkCreateImageView(device, &ivCreateInfo, NULL, &presentViews[i])); | |
} | |
VkAttachmentDescription attachments[2] = {}; | |
attachments[0].format = suitableFormat; | |
attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; | |
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; | |
attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; | |
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; | |
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; | |
attachments[0].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
attachments[0].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
attachments[1].format = depthFormat; | |
attachments[1].samples = VK_SAMPLE_COUNT_1_BIT; | |
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; | |
attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; | |
attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; | |
attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; | |
attachments[1].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; | |
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; | |
VkAttachmentReference colorReference = {}; | |
colorReference.attachment = 0; | |
colorReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
VkAttachmentReference depthStencilReference = {}; | |
depthStencilReference.attachment = 1; | |
depthStencilReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; | |
VkSubpassDescription subpassDesc = {}; | |
subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; | |
subpassDesc.flags = 0; | |
subpassDesc.inputAttachmentCount = 0; | |
subpassDesc.pInputAttachments = NULL; | |
subpassDesc.colorAttachmentCount = 1; | |
subpassDesc.pColorAttachments = &colorReference; | |
subpassDesc.pResolveAttachments = NULL; | |
subpassDesc.pDepthStencilAttachment = &depthStencilReference; | |
subpassDesc.preserveAttachmentCount = 0; | |
subpassDesc.pPreserveAttachments = NULL; | |
VkRenderPassCreateInfo rpCreateInfo = {}; | |
rpCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; | |
rpCreateInfo.pNext = NULL; | |
rpCreateInfo.flags = 0; | |
rpCreateInfo.attachmentCount = 2; | |
rpCreateInfo.pAttachments = attachments; | |
rpCreateInfo.subpassCount = 1; | |
rpCreateInfo.pSubpasses = &subpassDesc; | |
rpCreateInfo.dependencyCount = 0; | |
rpCreateInfo.pDependencies = NULL; | |
VkRenderPass renderPass; | |
VKCHECK(vkCreateRenderPass(device, &rpCreateInfo, NULL, &renderPass)); | |
VkImageView fbAttachments[2]; | |
fbAttachments[1] = dsImageView; | |
VkFramebufferCreateInfo fbCreateInfo = {}; | |
fbCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; | |
fbCreateInfo.pNext = NULL; | |
fbCreateInfo.flags = 0; | |
fbCreateInfo.renderPass = renderPass; | |
fbCreateInfo.attachmentCount = 2; | |
fbCreateInfo.pAttachments = fbAttachments; | |
fbCreateInfo.width = width; | |
fbCreateInfo.height = height; | |
fbCreateInfo.layers = 1; | |
VkFramebuffer framebuffers[2]; | |
for (uint32_t i = 0; i < 2; ++i) { | |
fbAttachments[0] = presentViews[i]; | |
VKCHECK(vkCreateFramebuffer(device, &fbCreateInfo, NULL, framebuffers + i)); | |
} | |
// flush preparation commands | |
VKCHECK(vkEndCommandBuffer(cmdBuf)); | |
VkSubmitInfo submitInfo = {}; | |
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; | |
submitInfo.pNext = NULL; | |
submitInfo.waitSemaphoreCount = 0; | |
submitInfo.pWaitSemaphores = NULL; | |
submitInfo.pWaitDstStageMask = NULL; | |
submitInfo.commandBufferCount = 1; | |
submitInfo.pCommandBuffers = &cmdBuf; | |
submitInfo.signalSemaphoreCount = 0; | |
submitInfo.pSignalSemaphores = NULL; | |
VKCHECK(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); | |
VKCHECK(vkQueueWaitIdle(queue)); | |
vkFreeCommandBuffers(device, cmdPool, 1, &cmdBuf); | |
cmdBuf = VK_NULL_HANDLE; | |
// and create new ones for rendering | |
VkCommandBufferAllocateInfo cmdBufAllocInfo = {}; | |
cmdBufAllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; | |
cmdBufAllocInfo.pNext = NULL; | |
cmdBufAllocInfo.commandPool = cmdPool; | |
cmdBufAllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; | |
cmdBufAllocInfo.commandBufferCount = 2; | |
VkCommandBuffer cmdBufs[2]; | |
VKCHECK(vkAllocateCommandBuffers(device, &cmdBufAllocInfo, cmdBufs)); | |
VkCommandBufferInheritanceInfo cmdBufInhInfo = {}; | |
cmdBufInhInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO; | |
cmdBufInhInfo.pNext = NULL; | |
cmdBufInhInfo.renderPass = VK_NULL_HANDLE; | |
cmdBufInhInfo.subpass = 0; | |
cmdBufInhInfo.framebuffer = VK_NULL_HANDLE; | |
cmdBufInhInfo.occlusionQueryEnable = VK_FALSE; | |
cmdBufInhInfo.queryFlags = 0; | |
cmdBufInhInfo.pipelineStatistics = 0; | |
VkCommandBufferBeginInfo cmdBufBeginInfo = {}; | |
cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; | |
cmdBufBeginInfo.pNext = NULL; | |
cmdBufBeginInfo.flags = 0; | |
cmdBufBeginInfo.pInheritanceInfo = &cmdBufInhInfo; | |
VkClearValue clearValues[2] = {}; | |
clearValues[0].color.float32[0] = 0.5f; | |
clearValues[0].color.float32[1] = 0.5f; | |
clearValues[0].color.float32[2] = 0.5f; | |
clearValues[0].color.float32[3] = 1.0f; | |
clearValues[1].depthStencil = (VkClearDepthStencilValue){ 1.0f, 0 }; | |
// build our drawing command buffers | |
for (uint32_t i = 0; i < 2; ++i) { | |
VKCHECK(vkBeginCommandBuffer(cmdBufs[i], &cmdBufBeginInfo)); | |
VkImageMemoryBarrier imageMemBarrier = {}; | |
imageMemBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; | |
imageMemBarrier.pNext = NULL; | |
imageMemBarrier.srcAccessMask = 0; | |
imageMemBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; | |
imageMemBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; | |
imageMemBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
imageMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
imageMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
imageMemBarrier.image = swapchainImages[i]; | |
imageMemBarrier.subresourceRange = (VkImageSubresourceRange){ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; | |
vkCmdPipelineBarrier(cmdBufs[i], VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, 1, &imageMemBarrier); | |
VkRenderPassBeginInfo rpBeginInfo = {}; | |
rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; | |
rpBeginInfo.pNext = NULL; | |
rpBeginInfo.renderPass = renderPass; | |
rpBeginInfo.framebuffer = framebuffers[i]; | |
rpBeginInfo.renderArea.offset.x = 0; | |
rpBeginInfo.renderArea.offset.y = 0; | |
rpBeginInfo.renderArea.extent.width = width; | |
rpBeginInfo.renderArea.extent.height = height; | |
rpBeginInfo.clearValueCount = 2; | |
rpBeginInfo.pClearValues = clearValues; | |
vkCmdBeginRenderPass(cmdBufs[i], &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE); | |
VkViewport viewport = {}; | |
viewport.width = (float)(width); | |
viewport.height = (float)(height); | |
viewport.minDepth = 0.0f; | |
viewport.maxDepth = 1.0f; | |
vkCmdSetViewport(cmdBufs[i], 0, 1, &viewport); | |
VkRect2D scissor = {}; | |
scissor.extent.width = width; | |
scissor.extent.height = height; | |
scissor.offset.x = 0; | |
scissor.offset.y = 0; | |
vkCmdSetScissor(cmdBufs[i], 0, 1, &scissor); | |
vkCmdEndRenderPass(cmdBufs[i]); | |
VkImageMemoryBarrier prePresentBarrier = {}; | |
prePresentBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; | |
prePresentBarrier.pNext = NULL; | |
prePresentBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; | |
prePresentBarrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; | |
prePresentBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; | |
prePresentBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; | |
prePresentBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
prePresentBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
prePresentBarrier.subresourceRange = (VkImageSubresourceRange){ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; | |
prePresentBarrier.image = swapchainImages[i]; | |
vkCmdPipelineBarrier(cmdBufs[i], VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, 1, &prePresentBarrier); | |
VKCHECK(vkEndCommandBuffer(cmdBufs[i])); | |
} | |
// main loop | |
while (!glfwWindowShouldClose(window)) { | |
glfwPollEvents(); | |
// do the rendering | |
VkSemaphore presentCompleteSemaphore; | |
VkSemaphoreCreateInfo pcsCreateInfo = {}; | |
pcsCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; | |
pcsCreateInfo.pNext = NULL; | |
pcsCreateInfo.flags = 0; | |
VKCHECK(vkCreateSemaphore(device, &pcsCreateInfo, NULL, &presentCompleteSemaphore)); | |
VkResult res = pfnAcquireNextImageKHR(device, swapchain, UINT64_MAX, presentCompleteSemaphore, VK_NULL_HANDLE, ¤tSwapchainImage); | |
if (res == VK_ERROR_OUT_OF_DATE_KHR) { | |
assert(false && "Swapchain is out-of-date!"); | |
} else if (res == VK_SUBOPTIMAL_KHR) { | |
} else { | |
assert(res == VK_SUCCESS); | |
} | |
VkSubmitInfo submitInfo = {}; | |
VkPipelineStageFlags pipelineStageFlags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; | |
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; | |
submitInfo.pNext = NULL; | |
submitInfo.waitSemaphoreCount = 1; | |
submitInfo.pWaitSemaphores = &presentCompleteSemaphore; | |
submitInfo.pWaitDstStageMask = &pipelineStageFlags; | |
submitInfo.commandBufferCount = 1; | |
submitInfo.pCommandBuffers = &cmdBufs[currentSwapchainImage]; | |
submitInfo.signalSemaphoreCount = 0; | |
submitInfo.pSignalSemaphores = NULL; | |
res = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE); | |
assert(res == VK_SUCCESS); | |
VkPresentInfoKHR present = {}; | |
present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; | |
present.pNext = NULL; | |
present.swapchainCount = 1; | |
present.pSwapchains = &swapchain; | |
present.pImageIndices = ¤tSwapchainImage; | |
res = pfnQueuePresentKHR(queue, &present); | |
if (res == VK_ERROR_OUT_OF_DATE_KHR) { | |
assert(false && "Swapchain out of date!"); | |
} else if (res == VK_SUBOPTIMAL_KHR) { | |
} else { | |
assert(res == VK_SUCCESS); | |
} | |
res = vkQueueWaitIdle(queue); | |
assert(res == VK_SUCCESS); | |
vkDestroySemaphore(device, presentCompleteSemaphore, NULL); | |
} | |
// free allocated resources | |
vkDestroyRenderPass(device, renderPass, NULL); | |
vkDestroyImageView(device, dsImageView, NULL); | |
vkDestroyImage(device, dsImage, NULL); | |
vkFreeMemory(device, dsMemory, NULL); | |
vkFreeCommandBuffers(device, cmdPool, 2, cmdBufs); | |
vkDestroyCommandPool(device, cmdPool, NULL); | |
for (uint32_t i = 0; i < 2; ++i) { | |
vkDestroyImageView(device, presentViews[i], NULL); | |
vkDestroyFramebuffer(device, framebuffers[i], NULL); | |
} | |
pfnDestroySwapchainKHR(device, swapchain, NULL); | |
pfnDestroySurfaceKHR(instance, surface, NULL); | |
glfwDestroyWindow(window); | |
pfnDestroyDevice(device, NULL); | |
if (validate) { | |
pfnDestroyDebugReportCallbackEXT(instance, debugReportCallback, NULL); | |
} | |
pfnDestroyInstance(instance, NULL); | |
glfwTerminate(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment