#define GLFW_INCLUDE_NONE #define GLFW_INCLUDE_VULKAN #include "window.hpp" #include "glm/ext/matrix_transform.hpp" #include "glm/gtc/matrix_transform.hpp" #define NELEMS(x) (sizeof(x) / sizeof((x)[0])) const bool ENABLE_VALIDATION_LAYERS = true; const bool VULKAN_DEBUG_REPORT = true; /* * Initialization */ GLFWwindow *window = nullptr; VkInstance vkInstance = nullptr; VkPhysicalDevice vkPhysicalDevice = nullptr; VkPhysicalDeviceProperties vkPhysicalDeviceProperties; VkPhysicalDeviceMemoryProperties vkPhysicalDeviceMemoryProperties; VkDevice vkDevice = nullptr; VkQueue graphicsQueue = nullptr; VkQueue presentQueue = nullptr; VkQueue transferQueue = nullptr; VkSurfaceKHR vkSurfaceKHR = nullptr; VkDebugReportCallbackEXT vkDebugReport = nullptr; VkAllocationCallbacks vkAllocatorStruct = {}; VkAllocationCallbacks *vkAllocator = nullptr; unsigned int graphicsFamilyIndex; unsigned int presentFamilyIndex; unsigned int transferFamilyIndex; /* * Instantiation */ bool shouldRecreateSwapchain = false; unsigned int CURRENT_FRAME = 0; unsigned int selectedSurfaceIndex = -1u; unsigned int selectedPresentIndex = -1u; unsigned int swapchainLength = 0u; VkSwapchainKHR vkSwapchainKHR = VK_NULL_HANDLE; VkSurfaceFormatKHR vkSurfaceFormatKHR; VkFormat depthFormat; VkPresentModeKHR vkPresentModeKHR; VkExtent2D extent; VkImage *swapchainImages = nullptr; VkImageView *swapchainImageViews = nullptr; VkImage *renderImages = nullptr; VkImageView *renderImageViews = nullptr; VkDeviceMemory renderImagesMemory; VkImage *colorImages = nullptr; VkImageView *colorImageViews = nullptr; VkDeviceMemory colorImagesMemory; VkImage *depthImages = nullptr; VkImageView *depthImageViews = nullptr; VkDeviceMemory depthImagesMemory; VkSampler presentSampler; VkSampleCountFlagBits renderSampleCount; VkRenderPass presentRenderPass; VkRenderPass renderRenderPass; VkDescriptorSetLayout vkDescriptorSetLayout; VkDescriptorPool presentDescriptorPool; VkDescriptorSet presentDescriptorSets[MAX_FRAMES_IN_FLIGHT]; VkPipelineLayout pipelineLayout; VkPipeline graphicsPipeline; VkFramebuffer *swapchainFramebuffers = nullptr; VkFramebuffer *renderImageFramebuffers = nullptr; VkCommandPool transferCommandPool; VkCommandBuffer transferCommandBuffer; VkCommandPool graphicsCommandPool; VkCommandBuffer graphicsCommandBuffer; VkCommandBuffer presentCommandBuffers[MAX_FRAMES_IN_FLIGHT]; VkSemaphore presentImageAvailableSemaphores[MAX_FRAMES_IN_FLIGHT]; VkSemaphore presentRenderFinishedSemaphores[MAX_FRAMES_IN_FLIGHT]; VkFence presentInFlightFences[MAX_FRAMES_IN_FLIGHT]; UniformBufferObject UBO{ .model = glm::mat4(1), .view = glm::lookAt(glm::vec3(0), glm::vec3(2, 2, 0), glm::vec3(0, 0, 1)), .proj = glm::mat4(1), }; VkDeviceMemory uniformBufferMemory; // public VkBuffer UniformBuffers[MAX_FRAMES_IN_FLIGHT]; /* * ImGui */ bool isImGuiRenderActive = true; VkDescriptorPool imGuiDescriptorPool; const std::vector REQUIRED_EXTENSIONS = std::vector { VK_KHR_SWAPCHAIN_EXTENSION_NAME }; static void ImGuiCheckVkResult(VkResult err) { if (err == 0) { return; } fprintf(stderr, "[imgui][vulkan] Error: VkResult = %d\n", err); if (err < 0) { abort(); } } VkBool32 UserDebugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData) { printf("Validation Layer: %s\n", pCallbackData->pMessage); return VK_FALSE; } static VKAPI_ATTR VkBool32 VKAPI_CALL DebugReport(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, unsigned long object, size_t location, int messageCode, const char *pLayerPrefix, const char *pMessage, void *pUserData) { (void)flags; (void)object; (void)location; (void)messageCode; (void)pUserData; (void)pLayerPrefix; // Unused arguments fprintf(stderr, "[vulkan] Debug report from ObjectType: %i\nMessage: %s\n\n", objectType, pMessage); return VK_FALSE; } unsigned int FindMemoryTypeIndex(uint32_t typeFilter, VkMemoryPropertyFlags memPropertyFlags) { for (uint32_t i = 0; i < vkPhysicalDeviceMemoryProperties.memoryTypeCount; i++) { if ((typeFilter & (1 << i)) && (vkPhysicalDeviceMemoryProperties.memoryTypes[i].propertyFlags & memPropertyFlags) == memPropertyFlags) { return i; } } fprintf(stdout, "[vulkan] Failed to find appropriate memory type index: typeFilter: %u - memPropertyFlags: %u\n", typeFilter, memPropertyFlags); return 0; } VkFormat FindSupportedFormat(long candidateCount, const VkFormat *candidates, VkImageTiling tiling, VkFormatFeatureFlags flags) { for (long i = 0; i < candidateCount; ++i) { VkFormatProperties props; vkGetPhysicalDeviceFormatProperties(vkPhysicalDevice, candidates[i], &props); if (tiling == VK_IMAGE_TILING_LINEAR && (props.linearTilingFeatures & flags) == flags) { return candidates[i]; } else if (tiling == VK_IMAGE_TILING_OPTIMAL && (props.optimalTilingFeatures & flags) == flags) { return candidates[i]; } } fprintf(stdout, "[vulkan] Failed to find appropriate format: tiling: %u - flags: %u\n", tiling, flags); return VkFormat(0); } void BeginTransferBuffer(VkDeviceSize requestedMemorySize, VkBuffer &buffer, VkDeviceMemory &deviceMemory, void *&deviceData) { VkBufferCreateInfo transferBufferCI; transferBufferCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; transferBufferCI.pNext = nullptr; transferBufferCI.flags = 0; transferBufferCI.size = requestedMemorySize; transferBufferCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; transferBufferCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; transferBufferCI.queueFamilyIndexCount = 1; transferBufferCI.pQueueFamilyIndices = &transferFamilyIndex; vkCreateBuffer(vkDevice, &transferBufferCI, vkAllocator, &buffer); VkMemoryRequirements memoryRequirements; vkGetBufferMemoryRequirements(vkDevice, buffer, &memoryRequirements); VkMemoryAllocateInfo transferMemAllocInfo; transferMemAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; transferMemAllocInfo.pNext = nullptr; transferMemAllocInfo.allocationSize = memoryRequirements.size; transferMemAllocInfo.memoryTypeIndex = FindMemoryTypeIndex(memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); vkAllocateMemory(vkDevice, &transferMemAllocInfo, vkAllocator, &deviceMemory); vkBindBufferMemory(vkDevice, buffer, deviceMemory, 0); vkMapMemory(vkDevice, deviceMemory, 0, transferMemAllocInfo.allocationSize, 0, &deviceData); } void EndTransferBuffer(VkBuffer &buffer, VkDeviceMemory &deviceMemory) { vkUnmapMemory(vkDevice, deviceMemory); vkDestroyBuffer(vkDevice, buffer, vkAllocator); vkFreeMemory(vkDevice, deviceMemory, vkAllocator); } unsigned int FindQueueFamilyIndex(VkPhysicalDevice device, short hasPresentSupport = -1, VkQueueFlagBits includeBits = (VkQueueFlagBits)0U, VkQueueFlagBits excludeBits = (VkQueueFlagBits)0U) { if (hasPresentSupport == -1 && includeBits == 0 && excludeBits == 0) { return 0U; } unsigned int queueFamilyPropertyCount = 0U; vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyPropertyCount, nullptr); auto *queueFamilyProperties = Pke_New(queueFamilyPropertyCount); vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyPropertyCount, queueFamilyProperties); for (unsigned int i = 0; i < queueFamilyPropertyCount; i++) { if (includeBits != 0 && (queueFamilyProperties[i].queueFlags & includeBits) == 0) { continue; } if (excludeBits != 0 && (queueFamilyProperties[i].queueFlags & excludeBits) != 0) { continue; } if (hasPresentSupport != -1) { VkBool32 presentSupport; vkGetPhysicalDeviceSurfaceSupportKHR(device, i, vkSurfaceKHR, &presentSupport); if (presentSupport != hasPresentSupport) { continue; } } return i; } Pke_Delete(queueFamilyProperties, queueFamilyPropertyCount); return 0xFFFFFFFF; } void InitVulkan() { if (ENABLE_VALIDATION_LAYERS) { unsigned int layerCount; vkEnumerateInstanceLayerProperties(&layerCount, nullptr); VkLayerProperties *availableLayerProperties = Pke_New(layerCount); vkEnumerateInstanceLayerProperties(&layerCount, availableLayerProperties); printf("Available Layers:\n"); for (long i = 0; i < layerCount; ++i) { printf("\t%s\n", availableLayerProperties[i].layerName); } Pke_Delete(availableLayerProperties, layerCount); } VkApplicationInfo appInfo; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.pNext = nullptr; appInfo.pApplicationName = "pikul"; appInfo.applicationVersion = 1; appInfo.pEngineName = "pikul"; appInfo.engineVersion = 1; appInfo.apiVersion = VK_API_VERSION_1_3; VkInstanceCreateInfo createInfo; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; createInfo.pNext = nullptr; createInfo.flags = {}; createInfo.pApplicationInfo = &appInfo; createInfo.enabledLayerCount = 0; std::vector enabledLayerNames(1); enabledLayerNames[0] = "VK_LAYER_KHRONOS_validation"; printf("Requested Layers:\n"); for (const char *s : enabledLayerNames) { printf("\t%s\n", s); } VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo; debugCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; debugCreateInfo.pNext = nullptr; debugCreateInfo.flags = {}; if (ENABLE_VALIDATION_LAYERS) { createInfo.enabledLayerCount = enabledLayerNames.size(); createInfo.ppEnabledLayerNames = enabledLayerNames.data(); debugCreateInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; debugCreateInfo.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; debugCreateInfo.pfnUserCallback = UserDebugCallback; debugCreateInfo.pUserData = nullptr; createInfo.pNext = &debugCreateInfo; } std::vector allGlfwExtensions; { unsigned int extensionCount = 0; const char **glfwExtensions = glfwGetRequiredInstanceExtensions(&extensionCount); allGlfwExtensions.reserve(extensionCount + 2); copy(&glfwExtensions[0], &glfwExtensions[extensionCount], back_inserter(allGlfwExtensions)); if (ENABLE_VALIDATION_LAYERS) { allGlfwExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); allGlfwExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME); } createInfo.enabledExtensionCount = allGlfwExtensions.size(); createInfo.ppEnabledExtensionNames = allGlfwExtensions.data(); } printf("Required Extensions:\n"); for (const auto &ext : allGlfwExtensions) { printf("\t%s\n", ext); } if (ENABLE_VALIDATION_LAYERS) { unsigned int extensionCount = 0; vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); auto *extensions = Pke_New(extensionCount); vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions); printf("Available Extensions:\n"); for (long i = 0; i < extensionCount; ++i) { printf("\t%s\n", extensions[i].extensionName); } Pke_Delete(extensions, extensionCount); } auto result = vkCreateInstance(&createInfo, nullptr, &vkInstance); if (result != VK_SUCCESS) { printf("Failed to create VkInstance! : %d\n", result); throw result; } if (VULKAN_DEBUG_REPORT) { PFN_vkCreateDebugReportCallbackEXT func = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(vkInstance, "vkCreateDebugReportCallbackEXT"); assert(func != nullptr && "vkCreateDebugReportCallbackEXT ProcAddr was nullptr"); VkDebugReportCallbackCreateInfoEXT debugReportCallbackCreateInfo; debugReportCallbackCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; debugReportCallbackCreateInfo.pNext = nullptr; debugReportCallbackCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; debugReportCallbackCreateInfo.pfnCallback = DebugReport; debugReportCallbackCreateInfo.pUserData = nullptr; result = func(vkInstance, &debugReportCallbackCreateInfo, nullptr, &vkDebugReport); if (result != VK_SUCCESS) { fprintf(stderr, "%s\n", "Failed to create debug report!"); } } // create surface if (glfwCreateWindowSurface(vkInstance, window, nullptr, &vkSurfaceKHR) != VK_SUCCESS) { throw "Failed to create window surface"; } // pick physical device unsigned int physicalDeviceCount = 0; vkEnumeratePhysicalDevices(vkInstance, &physicalDeviceCount, nullptr); assert(physicalDeviceCount > 0); auto *physicalDevices = Pke_New(physicalDeviceCount); vkEnumeratePhysicalDevices(vkInstance, &physicalDeviceCount, physicalDevices); graphicsFamilyIndex = 0; presentFamilyIndex = 0; for (long i = 0; i < physicalDeviceCount; ++i) { const auto &device = physicalDevices[i]; // check queue families graphicsFamilyIndex = FindQueueFamilyIndex(device, -1, VK_QUEUE_GRAPHICS_BIT); presentFamilyIndex = FindQueueFamilyIndex(device, 1); if (graphicsFamilyIndex == 0xFFFFFFFF || presentFamilyIndex == 0xFFFFFFFF) { continue; } // check device extension support std::vector requiredExtensions(REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end()); unsigned int extensionCount = 0; vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr); auto *extensionProperties = Pke_New(extensionCount); vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, extensionProperties); for (long k = 0; k < extensionCount; ++k) { const auto &property = extensionProperties[k]; auto it = requiredExtensions.begin(); while (it != requiredExtensions.end()) { if (strcmp(*it.base(), property.extensionName)) { requiredExtensions.erase(it); } else { it++; } } } Pke_Delete(extensionProperties, extensionCount); if (requiredExtensions.empty() == false) { continue; } // surface formats unsigned int surfaceCount = 0; vkGetPhysicalDeviceSurfaceFormatsKHR(device, vkSurfaceKHR, &surfaceCount, nullptr); if (surfaceCount == 0) { continue; } // check present modes unsigned int presentModeCount = 0; vkGetPhysicalDeviceSurfacePresentModesKHR(device, vkSurfaceKHR, &presentModeCount, nullptr); if (presentModeCount == 0) { continue; } vkPhysicalDevice = device; vkGetPhysicalDeviceProperties(vkPhysicalDevice, &vkPhysicalDeviceProperties); printf("Selected VkPhysicalDevice: %s\n", vkPhysicalDeviceProperties.deviceName); vkGetPhysicalDeviceMemoryProperties(vkPhysicalDevice, &vkPhysicalDeviceMemoryProperties); break; } assert(vkPhysicalDevice != nullptr && "Failed to find suitable physical device"); Pke_Delete(physicalDevices, physicalDeviceCount); // Create logical device { transferFamilyIndex = FindQueueFamilyIndex(vkPhysicalDevice, 0, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_GRAPHICS_BIT); if (transferFamilyIndex == 0xFFFFFFFF) { transferFamilyIndex = FindQueueFamilyIndex(vkPhysicalDevice, -1, VK_QUEUE_TRANSFER_BIT, VK_QUEUE_GRAPHICS_BIT); } if (transferFamilyIndex == 0xFFFFFFFF) { transferFamilyIndex = FindQueueFamilyIndex(vkPhysicalDevice, -1, VK_QUEUE_TRANSFER_BIT); } if (transferFamilyIndex == 0xFFFFFFFF) { printf("Failed to find transfer queue index"); throw("Failed to find transfer queue index"); } VkDeviceQueueCreateInfo deviceQueueCreateInfos[2]; float graphicsPriorities[1] = { 1.0f }; deviceQueueCreateInfos[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; deviceQueueCreateInfos[0].pNext = nullptr; deviceQueueCreateInfos[0].flags = 0; deviceQueueCreateInfos[0].queueFamilyIndex = graphicsFamilyIndex; deviceQueueCreateInfos[0].queueCount = 1l; deviceQueueCreateInfos[0].pQueuePriorities = graphicsPriorities; float transferPriorities[1] = { 0.5f }; deviceQueueCreateInfos[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; deviceQueueCreateInfos[1].pNext = nullptr; deviceQueueCreateInfos[1].flags = 0; deviceQueueCreateInfos[1].queueFamilyIndex = transferFamilyIndex; deviceQueueCreateInfos[1].queueCount = 1l; deviceQueueCreateInfos[1].pQueuePriorities = transferPriorities; VkPhysicalDeviceFeatures vkPhysicalDeviceFeatures; vkGetPhysicalDeviceFeatures(vkPhysicalDevice, &vkPhysicalDeviceFeatures); VkPhysicalDeviceFeatures requestedFeatures{}; // {} to initialize everything to 0 requestedFeatures.samplerAnisotropy = VK_TRUE; requestedFeatures.sampleRateShading = VK_TRUE; requestedFeatures.fillModeNonSolid = vkPhysicalDeviceFeatures.fillModeNonSolid; VkDeviceCreateInfo vkDeviceCreateInfo; vkDeviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; vkDeviceCreateInfo.pNext = nullptr; vkDeviceCreateInfo.flags = 0; vkDeviceCreateInfo.queueCreateInfoCount = 2; vkDeviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfos; if (ENABLE_VALIDATION_LAYERS) { vkDeviceCreateInfo.enabledLayerCount = enabledLayerNames.size(); vkDeviceCreateInfo.ppEnabledLayerNames = enabledLayerNames.data(); } vkDeviceCreateInfo.enabledExtensionCount = REQUIRED_EXTENSIONS.size(); vkDeviceCreateInfo.ppEnabledExtensionNames = REQUIRED_EXTENSIONS.data(); vkDeviceCreateInfo.pEnabledFeatures = &requestedFeatures; result = vkCreateDevice(vkPhysicalDevice, &vkDeviceCreateInfo, vkAllocator, &vkDevice); if (result != VK_SUCCESS) { printf("Failed to create VkInstance! : %d\n", result); throw result; } } // queues { vkGetDeviceQueue(vkDevice, graphicsFamilyIndex, 0, &graphicsQueue); vkGetDeviceQueue(vkDevice, presentFamilyIndex, 0, &presentQueue); vkGetDeviceQueue(vkDevice, transferFamilyIndex, 0, &transferQueue); } // surface formats { VkFormat acceptableDepthFormats[3] = { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT, }; depthFormat = FindSupportedFormat(3, acceptableDepthFormats, VK_IMAGE_TILING_OPTIMAL, VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT); } // sampler { VkSampleCountFlags counts = vkPhysicalDeviceProperties.limits.framebufferColorSampleCounts & vkPhysicalDeviceProperties.limits.framebufferDepthSampleCounts; if (counts & VK_SAMPLE_COUNT_64_BIT) { renderSampleCount = VK_SAMPLE_COUNT_64_BIT; } else if (counts & VK_SAMPLE_COUNT_32_BIT) { renderSampleCount = VK_SAMPLE_COUNT_32_BIT; } else if (counts & VK_SAMPLE_COUNT_16_BIT) { renderSampleCount = VK_SAMPLE_COUNT_16_BIT; } else if (counts & VK_SAMPLE_COUNT_8_BIT) { renderSampleCount = VK_SAMPLE_COUNT_8_BIT; } else if (counts & VK_SAMPLE_COUNT_4_BIT) { renderSampleCount = VK_SAMPLE_COUNT_4_BIT; } else if (counts & VK_SAMPLE_COUNT_2_BIT) { renderSampleCount = VK_SAMPLE_COUNT_2_BIT; } } // generic present sampler VkSamplerCreateInfo samplerCreateInfo; samplerCreateInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; samplerCreateInfo.pNext = nullptr; samplerCreateInfo.flags = 0U; samplerCreateInfo.magFilter = VK_FILTER_NEAREST; samplerCreateInfo.minFilter = VK_FILTER_NEAREST; samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; samplerCreateInfo.mipLodBias = 0.0f; samplerCreateInfo.anisotropyEnable = VK_FALSE; samplerCreateInfo.maxAnisotropy = 1.0f; samplerCreateInfo.compareEnable = VK_FALSE; samplerCreateInfo.compareOp = {}; samplerCreateInfo.minLod = 0.0f; samplerCreateInfo.maxLod = 1.0f; samplerCreateInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; samplerCreateInfo.unnormalizedCoordinates = {}; vkCreateSampler(vkDevice, &samplerCreateInfo, vkAllocator, &presentSampler); } void CreateImageResources_Inner(VkImageCreateInfo *imageCreateInfo, VkImageViewCreateInfo *imageViewCreateInfo, VkBufferUsageFlagBits bufferUsageFlagBits, VkBuffer *imagesBuffer, VkImage *images, VkImageView *imageViews, VkDeviceMemory *deviceMemory) { VkImage tmpImage; vkCreateImage(vkDevice, imageCreateInfo, vkAllocator, &tmpImage); VkMemoryRequirements imageMemoryRequirements; vkGetImageMemoryRequirements(vkDevice, tmpImage, &imageMemoryRequirements); VkDeviceSize paddedImageSize = imageMemoryRequirements.size + (imageMemoryRequirements.alignment - (imageMemoryRequirements.size % imageMemoryRequirements.alignment)); assert(paddedImageSize % imageMemoryRequirements.alignment == 0); vkDestroyImage(vkDevice, tmpImage, vkAllocator); VkMemoryAllocateInfo vkMemoryAllocateInfo{}; vkMemoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; vkMemoryAllocateInfo.pNext = nullptr; vkMemoryAllocateInfo.allocationSize = paddedImageSize * MAX_FRAMES_IN_FLIGHT; vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(imageMemoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, deviceMemory); for (long i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) { vkCreateImage(vkDevice, imageCreateInfo, vkAllocator, &images[i]); vkBindImageMemory(vkDevice, images[i], *deviceMemory, paddedImageSize * i); imageViewCreateInfo->image = images[i]; vkCreateImageView(vkDevice, imageViewCreateInfo, vkAllocator, &imageViews[i]); } } void CreateSwapchain() { VkSurfaceCapabilitiesKHR surfaceCapabilities; vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vkPhysicalDevice, vkSurfaceKHR, &surfaceCapabilities); assert(MAX_FRAMES_IN_FLIGHT >= surfaceCapabilities.minImageCount); assert(surfaceCapabilities.maxImageCount == 0 || MAX_FRAMES_IN_FLIGHT <= surfaceCapabilities.maxImageCount); if (selectedSurfaceIndex == -1u) { unsigned int surfaceFormatCounts; vkGetPhysicalDeviceSurfaceFormatsKHR(vkPhysicalDevice, vkSurfaceKHR, &surfaceFormatCounts, nullptr); VkSurfaceFormatKHR *surfaceFormats = Pke_New(surfaceFormatCounts); vkGetPhysicalDeviceSurfaceFormatsKHR(vkPhysicalDevice, vkSurfaceKHR, &surfaceFormatCounts, surfaceFormats); selectedSurfaceIndex = 0; for (long i = 0; i < surfaceFormatCounts; ++i) { if (surfaceFormats[i].format != VkFormat::VK_FORMAT_B8G8R8A8_SRGB) continue; if (surfaceFormats[i].colorSpace != VkColorSpaceKHR::VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) continue; selectedSurfaceIndex = i; vkSurfaceFormatKHR.colorSpace = surfaceFormats[i].colorSpace; vkSurfaceFormatKHR.format = surfaceFormats[i].format; break; } Pke_Delete(surfaceFormats, surfaceFormatCounts); } int width, height; glfwGetFramebufferSize(window, &width, &height); extent.width = width; extent.height = height; // clamp width = extent.width < surfaceCapabilities.minImageExtent.width ? surfaceCapabilities.minImageExtent.width : extent.width; extent.width = width > surfaceCapabilities.maxImageExtent.width ? surfaceCapabilities.maxImageExtent.width : width; height = extent.height < surfaceCapabilities.minImageExtent.height ? surfaceCapabilities.minImageExtent.height : extent.height; extent.height = height > surfaceCapabilities.maxImageExtent.height ? surfaceCapabilities.maxImageExtent.height : height; if (selectedPresentIndex == -1u) { unsigned int presentModeCount = 0; vkGetPhysicalDeviceSurfacePresentModesKHR(vkPhysicalDevice, vkSurfaceKHR, &presentModeCount, nullptr); VkPresentModeKHR *presentModes = Pke_New(presentModeCount); vkGetPhysicalDeviceSurfacePresentModesKHR(vkPhysicalDevice, vkSurfaceKHR, &presentModeCount, presentModes); unsigned long mailboxIndex = -1; unsigned long fifoRelaxedIndex = -1; for (long i = 0; i < presentModeCount; ++i) { if (presentModes[i] != VkPresentModeKHR::VK_PRESENT_MODE_MAILBOX_KHR) { mailboxIndex = i; } else if (presentModes[i] != VkPresentModeKHR::VK_PRESENT_MODE_FIFO_RELAXED_KHR) { fifoRelaxedIndex = i; } } selectedPresentIndex = mailboxIndex != -1ul ? mailboxIndex : fifoRelaxedIndex; vkPresentModeKHR = presentModes[selectedPresentIndex]; Pke_Delete(presentModes, presentModeCount); } VkSwapchainCreateInfoKHR vkSwapchainCreateInfo; vkSwapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; vkSwapchainCreateInfo.pNext = nullptr; vkSwapchainCreateInfo.flags = 0; vkSwapchainCreateInfo.surface = vkSurfaceKHR; vkSwapchainCreateInfo.minImageCount = MAX_FRAMES_IN_FLIGHT; vkSwapchainCreateInfo.imageFormat = vkSurfaceFormatKHR.format; vkSwapchainCreateInfo.imageColorSpace = vkSurfaceFormatKHR.colorSpace; vkSwapchainCreateInfo.imageExtent = extent; vkSwapchainCreateInfo.imageArrayLayers = 1; vkSwapchainCreateInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; // vkSwapchainCreateInfo.imageSharingMode = {}; vkSwapchainCreateInfo.queueFamilyIndexCount = 0; vkSwapchainCreateInfo.pQueueFamilyIndices = nullptr; vkSwapchainCreateInfo.preTransform = surfaceCapabilities.currentTransform; vkSwapchainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; vkSwapchainCreateInfo.presentMode = vkPresentModeKHR; vkSwapchainCreateInfo.clipped = VK_TRUE; // vkSwapchainCreateInfo.oldSwapchain = vkSwapchainKHR; vkSwapchainCreateInfo.oldSwapchain = VK_NULL_HANDLE; unsigned int qfi[2] = { graphicsFamilyIndex, presentFamilyIndex }; if (graphicsFamilyIndex != presentFamilyIndex) { vkSwapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT; vkSwapchainCreateInfo.queueFamilyIndexCount = 2; vkSwapchainCreateInfo.pQueueFamilyIndices = qfi; } else { vkSwapchainCreateInfo.imageSharingMode = VkSharingMode::VK_SHARING_MODE_EXCLUSIVE; vkSwapchainCreateInfo.queueFamilyIndexCount = 0; } vkCreateSwapchainKHR(vkDevice, &vkSwapchainCreateInfo, vkAllocator, &vkSwapchainKHR); VkImageSubresourceRange vkImageSubresourceRange; vkImageSubresourceRange.aspectMask = VkImageAspectFlagBits::VK_IMAGE_ASPECT_COLOR_BIT; vkImageSubresourceRange.baseMipLevel = 0u; vkImageSubresourceRange.levelCount = 1u; vkImageSubresourceRange.baseArrayLayer = 0u; vkImageSubresourceRange.layerCount = 1u; VkImageViewCreateInfo vkImageViewCreateInfo; vkImageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; vkImageViewCreateInfo.pNext = nullptr; vkImageViewCreateInfo.flags = 0; vkImageViewCreateInfo.viewType = VkImageViewType::VK_IMAGE_VIEW_TYPE_2D; vkImageViewCreateInfo.format = vkSurfaceFormatKHR.format; vkImageViewCreateInfo.components = { VkComponentSwizzle::VK_COMPONENT_SWIZZLE_IDENTITY, VkComponentSwizzle::VK_COMPONENT_SWIZZLE_IDENTITY, VkComponentSwizzle::VK_COMPONENT_SWIZZLE_IDENTITY, VkComponentSwizzle::VK_COMPONENT_SWIZZLE_IDENTITY, }; vkImageViewCreateInfo.subresourceRange = vkImageSubresourceRange; vkGetSwapchainImagesKHR(vkDevice, vkSwapchainKHR, &swapchainLength, nullptr); swapchainImages = Pke_New(swapchainLength); vkGetSwapchainImagesKHR(vkDevice, vkSwapchainKHR, &swapchainLength, swapchainImages); swapchainImageViews = Pke_New(swapchainLength); for (long i = 0; i < swapchainLength; ++i) { vkImageViewCreateInfo.image = swapchainImages[i]; vkCreateImageView(vkDevice, &vkImageViewCreateInfo, vkAllocator, &swapchainImageViews[i]); } // render target items VkImageCreateInfo renderTargetImageCI{}; renderTargetImageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; renderTargetImageCI.pNext = nullptr; renderTargetImageCI.flags = 0; renderTargetImageCI.imageType = VK_IMAGE_TYPE_2D; renderTargetImageCI.format = vkSurfaceFormatKHR.format; renderTargetImageCI.extent.width = extent.width; renderTargetImageCI.extent.height = extent.height; renderTargetImageCI.extent.depth = 1; renderTargetImageCI.mipLevels = 1; renderTargetImageCI.arrayLayers = 1; renderTargetImageCI.samples = VK_SAMPLE_COUNT_1_BIT; renderTargetImageCI.tiling = VK_IMAGE_TILING_OPTIMAL; renderTargetImageCI.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; renderTargetImageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; renderTargetImageCI.queueFamilyIndexCount = 0; renderTargetImageCI.pQueueFamilyIndices = nullptr; renderTargetImageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; // render images renderImages = Pke_New(MAX_FRAMES_IN_FLIGHT); renderImageViews = Pke_New(MAX_FRAMES_IN_FLIGHT); CreateImageResources_Inner(&renderTargetImageCI, &vkImageViewCreateInfo, VkBufferUsageFlagBits(0), nullptr, renderImages, renderImageViews, &renderImagesMemory); // color images colorImages = Pke_New(MAX_FRAMES_IN_FLIGHT); colorImageViews = Pke_New(MAX_FRAMES_IN_FLIGHT); renderTargetImageCI.samples = renderSampleCount; renderTargetImageCI.usage = VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; CreateImageResources_Inner(&renderTargetImageCI, &vkImageViewCreateInfo, VkBufferUsageFlagBits(0), nullptr, colorImages, colorImageViews, &colorImagesMemory); // depth images depthImages = Pke_New(MAX_FRAMES_IN_FLIGHT); depthImageViews = Pke_New(MAX_FRAMES_IN_FLIGHT); renderTargetImageCI.format = depthFormat; vkImageViewCreateInfo.format = depthFormat; renderTargetImageCI.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; vkImageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; CreateImageResources_Inner(&renderTargetImageCI, &vkImageViewCreateInfo, VkBufferUsageFlagBits(0), nullptr, depthImages, depthImageViews, &depthImagesMemory); } void UpdatePresentDescriptorSets() { VkWriteDescriptorSet writeDescriptorSets[MAX_FRAMES_IN_FLIGHT]; VkDescriptorImageInfo descriptorImageInfo[MAX_FRAMES_IN_FLIGHT]; for (long i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) { writeDescriptorSets[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[i].pNext = nullptr; writeDescriptorSets[i].dstBinding = 0U; writeDescriptorSets[i].dstArrayElement = 0U; writeDescriptorSets[i].descriptorCount = 1; writeDescriptorSets[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeDescriptorSets[i].pBufferInfo = nullptr; writeDescriptorSets[i].pTexelBufferView = nullptr; descriptorImageInfo[i].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; descriptorImageInfo[i].sampler = presentSampler; descriptorImageInfo[i].imageView = renderImageViews[i]; writeDescriptorSets[i].pImageInfo = &descriptorImageInfo[i]; writeDescriptorSets[i].dstSet = presentDescriptorSets[i]; } vkUpdateDescriptorSets(vkDevice, MAX_FRAMES_IN_FLIGHT, writeDescriptorSets, 0, nullptr); } void UpdateCameraProjection() { UBO.proj = glm::perspective(glm::radians(45.f), extent.width / (float)extent.height, 0.1f, 100.f); UBO.proj[1][1] *= -1; } void CreateRenderPass() { VkAttachmentDescription colorAttachment; colorAttachment.flags = {}; colorAttachment.format = vkSurfaceFormatKHR.format; colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT; colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; VkAttachmentReference colorAttachmentRef; colorAttachmentRef.attachment = 0; colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkSubpassDescription subpass; subpass.flags = {}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.inputAttachmentCount = 0; subpass.pInputAttachments = nullptr; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &colorAttachmentRef; subpass.pResolveAttachments = nullptr; subpass.pDepthStencilAttachment = nullptr; subpass.preserveAttachmentCount = 0; subpass.pPreserveAttachments = nullptr; VkSubpassDependency subpassDependencies[2]; for (long i = 0; i < 2; ++i) { subpassDependencies[i].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; } subpassDependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; subpassDependencies[0].dstSubpass = 0U; subpassDependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; subpassDependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; subpassDependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; subpassDependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; subpassDependencies[1].srcSubpass = 0U; subpassDependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; subpassDependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; subpassDependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; subpassDependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; subpassDependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; VkRenderPassCreateInfo renderPassInfo; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; renderPassInfo.pNext = nullptr; renderPassInfo.flags = {}; renderPassInfo.attachmentCount = 1; renderPassInfo.pAttachments = &colorAttachment; renderPassInfo.subpassCount = 1; renderPassInfo.pSubpasses = &subpass; renderPassInfo.dependencyCount = 2; renderPassInfo.pDependencies = subpassDependencies; if (vkCreateRenderPass(vkDevice, &renderPassInfo, vkAllocator, &presentRenderPass) != VK_SUCCESS) { throw "failed to create present render pass!"; } colorAttachment.samples = renderSampleCount; colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkAttachmentDescription depthAttachment; depthAttachment.flags = 0; depthAttachment.format = depthFormat; depthAttachment.samples = renderSampleCount; depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkAttachmentDescription colorAttachmentResolve{colorAttachment}; colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT; colorAttachmentResolve.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; colorAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE; colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkAttachmentReference depthAttachmentRef; depthAttachmentRef.attachment = 1; depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; VkAttachmentReference colorAttachmentReseolveRef; colorAttachmentReseolveRef.attachment = 2; colorAttachmentReseolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; subpass.pColorAttachments = &colorAttachmentRef; subpass.pResolveAttachments = &colorAttachmentReseolveRef; subpass.pDepthStencilAttachment = &depthAttachmentRef; subpassDependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; subpassDependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; subpassDependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT; subpassDependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; subpassDependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; subpassDependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; subpassDependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; subpassDependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; VkAttachmentDescription attachments[3] = { colorAttachment, depthAttachment, colorAttachmentResolve }; renderPassInfo.attachmentCount = 3; renderPassInfo.pAttachments = attachments; renderPassInfo.dependencyCount = 2; renderPassInfo.pDependencies = subpassDependencies; if (vkCreateRenderPass(vkDevice, &renderPassInfo, vkAllocator, &renderRenderPass) != VK_SUCCESS) { throw "failed to create render pass!"; } } void CreateGraphicsPipeline() { AssetHandle vertShaderAsset = AM_Register("assets/shaders/present.vert.spv"); AssetHandle fragShaderAsset = AM_Register("assets/shaders/present.frag.spv"); auto vertShader = UploadShader(vertShaderAsset); auto fragShader = UploadShader(fragShaderAsset); VkPipelineShaderStageCreateInfo shaderStages[2]; for (long i = 0; i < 2; ++i) { shaderStages[i].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shaderStages[i].pNext = nullptr; shaderStages[i].flags = 0; shaderStages[i].pName = "main"; shaderStages[i].pSpecializationInfo = nullptr; } shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; shaderStages[0].module = vertShader; shaderStages[1].module = fragShader; VkDynamicState dynamicStates[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamicState; dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamicState.pNext = nullptr; dynamicState.flags = {}; dynamicState.dynamicStateCount = 2; dynamicState.pDynamicStates = dynamicStates; VkPipelineVertexInputStateCreateInfo vertexInputInfo; vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertexInputInfo.pNext = nullptr; vertexInputInfo.flags = {}; vertexInputInfo.vertexBindingDescriptionCount = 0; vertexInputInfo.pVertexBindingDescriptions = nullptr; vertexInputInfo.vertexAttributeDescriptionCount = 0; vertexInputInfo.pVertexAttributeDescriptions = nullptr; VkPipelineInputAssemblyStateCreateInfo inputAssembly; inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; inputAssembly.pNext = nullptr; inputAssembly.flags = {}; inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; inputAssembly.primitiveRestartEnable = VK_FALSE; VkViewport viewport; viewport.x = 0.0f; viewport.y = 0.0f; viewport.width = (float) extent.width; viewport.height = (float) extent.height; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; VkRect2D scissor; scissor.offset = {0, 0}; scissor.extent = extent; VkPipelineViewportStateCreateInfo viewportState; viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewportState.pNext = nullptr; viewportState.flags = {}; viewportState.viewportCount = 1; viewportState.pViewports = &viewport; viewportState.scissorCount = 1; viewportState.pScissors = &scissor; VkPipelineRasterizationStateCreateInfo rasterizer; rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterizer.pNext = nullptr; rasterizer.flags = {}; rasterizer.depthClampEnable = VK_FALSE; rasterizer.rasterizerDiscardEnable = VK_FALSE; rasterizer.polygonMode = VK_POLYGON_MODE_FILL; rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE; rasterizer.depthBiasEnable = VK_FALSE; rasterizer.depthBiasConstantFactor = 0.0f; rasterizer.depthBiasClamp = 0.0f; rasterizer.depthBiasSlopeFactor = 0.0f; rasterizer.lineWidth = 1.0f; VkPipelineMultisampleStateCreateInfo multisampling; multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; multisampling.pNext = nullptr; multisampling.flags = {}; multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; multisampling.sampleShadingEnable = VK_FALSE; multisampling.minSampleShading = 1.0f; multisampling.pSampleMask = nullptr; multisampling.alphaToCoverageEnable = VK_FALSE; multisampling.alphaToOneEnable = VK_FALSE; // TODO VkPipelineDepthStencilStateCreateInfo *depthStencilInfo = nullptr; VkPipelineColorBlendAttachmentState colorBlendAttachment; colorBlendAttachment.blendEnable = VK_FALSE; colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO; colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; VkPipelineColorBlendStateCreateInfo colorBlending; colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; colorBlending.pNext = nullptr; colorBlending.flags = {}; colorBlending.logicOpEnable = VK_FALSE; colorBlending.logicOp = VK_LOGIC_OP_COPY; colorBlending.attachmentCount = 1; colorBlending.pAttachments = &colorBlendAttachment; colorBlending.blendConstants[0] = 0.0f; colorBlending.blendConstants[1] = 0.0f; colorBlending.blendConstants[2] = 0.0f; colorBlending.blendConstants[3] = 0.0f; /* * PipelineLayout + DescriptorSets */ VkDescriptorSetLayoutBinding imageSamplerLayoutBinding; imageSamplerLayoutBinding.binding = 0; imageSamplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; imageSamplerLayoutBinding.descriptorCount = 1; imageSamplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; imageSamplerLayoutBinding.pImmutableSamplers = nullptr; VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo; descriptorSetLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; descriptorSetLayoutCreateInfo.pNext = nullptr; descriptorSetLayoutCreateInfo.flags = 0; descriptorSetLayoutCreateInfo.bindingCount = 1; descriptorSetLayoutCreateInfo.pBindings = &imageSamplerLayoutBinding; auto result = vkCreateDescriptorSetLayout(vkDevice, &descriptorSetLayoutCreateInfo, vkAllocator, &vkDescriptorSetLayout); if (result != VK_SUCCESS) { throw "failed to create descriptor set layout"; } VkDescriptorPoolSize vkDescriptorPoolSize; vkDescriptorPoolSize.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; vkDescriptorPoolSize.descriptorCount = swapchainLength; VkDescriptorPoolCreateInfo descriptorPoolCreateInfo; descriptorPoolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; descriptorPoolCreateInfo.pNext = nullptr; descriptorPoolCreateInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; descriptorPoolCreateInfo.maxSets = MAX_FRAMES_IN_FLIGHT; descriptorPoolCreateInfo.poolSizeCount = 1; descriptorPoolCreateInfo.pPoolSizes = &vkDescriptorPoolSize; vkCreateDescriptorPool(vkDevice, &descriptorPoolCreateInfo, vkAllocator, &presentDescriptorPool); VkDescriptorSetLayout setLayouts[MAX_FRAMES_IN_FLIGHT]; for (long i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) { setLayouts[i] = vkDescriptorSetLayout; } VkDescriptorSetAllocateInfo allocInfo; allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; allocInfo.pNext = nullptr; allocInfo.descriptorPool = presentDescriptorPool; allocInfo.descriptorSetCount = MAX_FRAMES_IN_FLIGHT; allocInfo.pSetLayouts = setLayouts; result = vkAllocateDescriptorSets(vkDevice, &allocInfo, presentDescriptorSets); if (result != VK_SUCCESS) { throw "failed to allocate present descriptor sets"; } VkPipelineLayoutCreateInfo pipelineLayoutInfo; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.pNext = nullptr; pipelineLayoutInfo.flags = {}; pipelineLayoutInfo.setLayoutCount = 1; pipelineLayoutInfo.pSetLayouts = &vkDescriptorSetLayout; pipelineLayoutInfo.pushConstantRangeCount = 0; pipelineLayoutInfo.pPushConstantRanges = nullptr; result = vkCreatePipelineLayout(vkDevice, &pipelineLayoutInfo, vkAllocator, &pipelineLayout); if (result != VK_SUCCESS) { throw "failed to create pipeline layout"; } VkGraphicsPipelineCreateInfo pipelineInfo; pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipelineInfo.pNext = nullptr; pipelineInfo.flags = {}; pipelineInfo.stageCount = 2; pipelineInfo.pStages = shaderStages; pipelineInfo.pVertexInputState = &vertexInputInfo; pipelineInfo.pInputAssemblyState = &inputAssembly; pipelineInfo.pTessellationState = nullptr; pipelineInfo.pViewportState = &viewportState; pipelineInfo.pRasterizationState = &rasterizer; pipelineInfo.pMultisampleState = &multisampling; pipelineInfo.pDepthStencilState = depthStencilInfo; pipelineInfo.pColorBlendState = &colorBlending; pipelineInfo.pDynamicState = &dynamicState; pipelineInfo.layout = pipelineLayout; pipelineInfo.renderPass = presentRenderPass; pipelineInfo.subpass = 0; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; pipelineInfo.basePipelineIndex = {}; result = vkCreateGraphicsPipelines(vkDevice, VK_NULL_HANDLE, 1, &pipelineInfo, vkAllocator, &graphicsPipeline); if (result != VK_SUCCESS) { throw "failed to create graphics pipeline."; } vkDestroyShaderModule(vkDevice, fragShader, vkAllocator); vkDestroyShaderModule(vkDevice, vertShader, vkAllocator); AM_Destroy(fragShaderAsset); AM_Destroy(vertShaderAsset); } void CreateFramebuffers() { assert(swapchainFramebuffers == nullptr || swapchainFramebuffers == CAFE_BABE(VkFramebuffer)); assert(renderImageFramebuffers == nullptr || renderImageFramebuffers == CAFE_BABE(VkFramebuffer)); swapchainFramebuffers = Pke_New(swapchainLength); renderImageFramebuffers = Pke_New(swapchainLength); VkFramebufferCreateInfo framebufferInfo; framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebufferInfo.pNext = nullptr; framebufferInfo.flags = {}; framebufferInfo.renderPass = presentRenderPass; framebufferInfo.attachmentCount = 1; framebufferInfo.pAttachments = nullptr; framebufferInfo.width = extent.width; framebufferInfo.height = extent.height; framebufferInfo.layers = 1; for (long i = 0; i < swapchainLength; ++i) { VkImageView attachments[] = { swapchainImageViews[i] }; framebufferInfo.pAttachments = attachments; auto result = vkCreateFramebuffer(vkDevice, &framebufferInfo, vkAllocator, &swapchainFramebuffers[i]); if (result != VK_SUCCESS) { throw "failed to create framebuffer"; } } framebufferInfo.attachmentCount = 3; framebufferInfo.renderPass = renderRenderPass; for (long i = 0; i < swapchainLength; ++i) { VkImageView attachments[] = { colorImageViews[i], depthImageViews[i], renderImageViews[i], }; framebufferInfo.pAttachments = attachments; auto result = vkCreateFramebuffer(vkDevice, &framebufferInfo, vkAllocator, &renderImageFramebuffers[i]); if (result != VK_SUCCESS) { throw "failed to create framebuffer"; } } } void CreateCommandPool() { VkCommandPoolCreateInfo poolInfo; poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; poolInfo.pNext = nullptr; poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; poolInfo.queueFamilyIndex = graphicsFamilyIndex; auto result = vkCreateCommandPool(vkDevice, &poolInfo, vkAllocator, &graphicsCommandPool); if (result != VK_SUCCESS) { throw "failed to create command pool"; } poolInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; poolInfo.queueFamilyIndex = transferFamilyIndex; result = vkCreateCommandPool(vkDevice, &poolInfo, vkAllocator, &transferCommandPool); if (result != VK_SUCCESS) { throw "failed to create transfer command pool"; } } void CreateCommandBuffer() { VkCommandBufferAllocateInfo allocInfo; allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocInfo.pNext = nullptr; allocInfo.commandPool = graphicsCommandPool; allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocInfo.commandBufferCount = MAX_FRAMES_IN_FLIGHT; auto result = vkAllocateCommandBuffers(vkDevice, &allocInfo, presentCommandBuffers); if (result != VK_SUCCESS) { throw "failed to allocate command buffer"; } allocInfo.commandBufferCount = 1; result = vkAllocateCommandBuffers(vkDevice, &allocInfo, &graphicsCommandBuffer); if (result != VK_SUCCESS) { throw "failed to allocate command buffer"; } allocInfo.commandPool = transferCommandPool; result = vkAllocateCommandBuffers(vkDevice, &allocInfo, &transferCommandBuffer); if (result != VK_SUCCESS) { throw "failed to allocate command buffer"; } } void CreateUniformBuffers() { uint32_t queueFamilyIndexes[2] = {graphicsFamilyIndex, transferFamilyIndex}; VkBufferCreateInfo vkBufferCreateInfo; vkBufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; vkBufferCreateInfo.pNext = nullptr; vkBufferCreateInfo.flags = {}; vkBufferCreateInfo.size = sizeof(UniformBufferObject); vkBufferCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; vkBufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; vkBufferCreateInfo.queueFamilyIndexCount = graphicsFamilyIndex == transferFamilyIndex ? 1 : 2; vkBufferCreateInfo.pQueueFamilyIndices = queueFamilyIndexes; vkCreateBuffer(vkDevice, &vkBufferCreateInfo, vkAllocator, &UniformBuffers[0]); VkMemoryRequirements memReqs; vkGetBufferMemoryRequirements(vkDevice, UniformBuffers[0], &memReqs); VkMemoryAllocateInfo vkMemoryAllocateInfo; vkMemoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; vkMemoryAllocateInfo.pNext = nullptr; vkMemoryAllocateInfo.allocationSize = memReqs.size * MAX_FRAMES_IN_FLIGHT; vkMemoryAllocateInfo.memoryTypeIndex = FindMemoryTypeIndex(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); vkAllocateMemory(vkDevice, &vkMemoryAllocateInfo, vkAllocator, &uniformBufferMemory); vkDestroyBuffer(vkDevice, UniformBuffers[0], vkAllocator); vkBufferCreateInfo.size = memReqs.size; for (long i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) { vkCreateBuffer(vkDevice, &vkBufferCreateInfo, vkAllocator, &UniformBuffers[i]); vkBindBufferMemory(vkDevice, UniformBuffers[i], uniformBufferMemory, memReqs.size * i); } } void CreateSyncObjects() { VkSemaphoreCreateInfo semaphoreInfo; semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; semaphoreInfo.pNext = nullptr; semaphoreInfo.flags = {}; VkFenceCreateInfo fenceInfo; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.pNext = nullptr; fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT; for (long i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) { auto result1 = vkCreateSemaphore(vkDevice, &semaphoreInfo, vkAllocator, &presentImageAvailableSemaphores[i]); auto result2 = vkCreateSemaphore(vkDevice, &semaphoreInfo, vkAllocator, &presentRenderFinishedSemaphores[i]); auto result3 = vkCreateFence(vkDevice, &fenceInfo, vkAllocator, &presentInFlightFences[i]); auto result = result1 == result2 && result2 == result3 && result3 == VK_SUCCESS; if (!result) { throw "failed to create sync objects"; } } } void CreateImGui() { IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; ImGui::StyleColorsDark(); VkDescriptorPoolSize poolSizes[] = { {VK_DESCRIPTOR_TYPE_SAMPLER, 1000}, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000}, {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000}, {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000}, {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000}, {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000}, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000}, {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000}, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000}, {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000}, {VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000}, }; VkDescriptorPoolCreateInfo descriptorPoolCreateInfo; descriptorPoolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; descriptorPoolCreateInfo.pNext = nullptr; descriptorPoolCreateInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; descriptorPoolCreateInfo.maxSets = 1000 * IM_ARRAYSIZE(poolSizes), descriptorPoolCreateInfo.poolSizeCount = static_cast(IM_ARRAYSIZE(poolSizes)); descriptorPoolCreateInfo.pPoolSizes = poolSizes; vkCreateDescriptorPool(vkDevice, &descriptorPoolCreateInfo, vkAllocator, &imGuiDescriptorPool); ImGui_ImplGlfw_InitForVulkan(window, true); ImGui_ImplVulkan_InitInfo initInfo; initInfo.Allocator = vkAllocator; initInfo.CheckVkResultFn = ImGuiCheckVkResult; initInfo.ColorAttachmentFormat = VkFormat::VK_FORMAT_B8G8R8A8_SRGB; initInfo.DescriptorPool = imGuiDescriptorPool; initInfo.Device = vkDevice; initInfo.Instance = vkInstance; initInfo.ImageCount = MAX_FRAMES_IN_FLIGHT; initInfo.MinImageCount = MAX_FRAMES_IN_FLIGHT; initInfo.MSAASamples = VK_SAMPLE_COUNT_1_BIT; initInfo.PhysicalDevice = vkPhysicalDevice; initInfo.PipelineCache = {}; initInfo.Queue = graphicsQueue; initInfo.QueueFamily = graphicsFamilyIndex; initInfo.Subpass = 0; initInfo.UseDynamicRendering = false; ImGui_ImplVulkan_Init(&initInfo, presentRenderPass); // font { VkCommandBufferBeginInfo begInfo; begInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; begInfo.pNext = nullptr; begInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; begInfo.pInheritanceInfo = {}; vkBeginCommandBuffer(presentCommandBuffers[0], &begInfo); ImGui_ImplVulkan_CreateFontsTexture(presentCommandBuffers[0]); vkEndCommandBuffer(presentCommandBuffers[0]); VkSubmitInfo submitInfo; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pNext = nullptr; submitInfo.waitSemaphoreCount = 0; submitInfo.pWaitSemaphores = nullptr; submitInfo.pWaitDstStageMask = nullptr; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = presentCommandBuffers; submitInfo.signalSemaphoreCount = 0; submitInfo.pSignalSemaphores = nullptr; vkQueueSubmit(graphicsQueue, 1, &submitInfo, nullptr); vkQueueWaitIdle(graphicsQueue); } ImGui_ImplVulkan_DestroyFontUploadObjects(); } void RecordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex) { vkResetCommandBuffer(commandBuffer, 0); VkCommandBufferBeginInfo beginInfo; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; beginInfo.flags = 0; beginInfo.pInheritanceInfo = nullptr; auto result = vkBeginCommandBuffer(commandBuffer, &beginInfo); if (result != VK_SUCCESS) { throw "failed to begin recording command buffer"; } VkClearColorValue clearColor = {{0.0f, 0.0f, 0.0f, 1.0f}}; VkClearDepthStencilValue clearDepth; clearDepth.depth = 1.0; clearDepth.stencil = 0; VkClearValue clearValues[2] = { VkClearValue { .color = clearColor }, VkClearValue { .depthStencil = clearDepth }, }; VkRenderPassBeginInfo renderPassInfo; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; renderPassInfo.renderPass = renderRenderPass; renderPassInfo.framebuffer = renderImageFramebuffers[imageIndex]; renderPassInfo.renderArea.offset = {0, 0}; renderPassInfo.renderArea.extent = extent; renderPassInfo.clearValueCount = 2; renderPassInfo.pClearValues = clearValues; renderPassInfo.pNext = VK_NULL_HANDLE; vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); VkViewport viewport; viewport.x = 0.0f; viewport.y = 0.0f; viewport.width = (float)extent.width; viewport.height = (float)extent.height; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; VkRect2D scissor; scissor.offset = {0, 0}; scissor.extent = extent; vkCmdSetViewport(commandBuffer, 0, 1, &viewport); vkCmdSetScissor(commandBuffer, 0, 1, &scissor); VkDeviceSize offsets[1] = {0U}; const uint64_t bindBucketCount = ECS_GetGrBinds_BucketCount(); for (long b = 0; b < bindBucketCount; ++b) { uint64_t itemCount; CompGrBinds *items = ECS_GetGrBinds(b, itemCount); for (long i = 0; i < itemCount; ++i) { CompGrBinds *binder = &items[i]; if (!binder->vkPipelineLayout) continue; if (binder->instanceBindingCount < 1) { continue; } vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, binder->graphicsPipeline); vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, binder->vkPipelineLayout, 0, 1, &binder->vkDescriptorSets[CURRENT_FRAME], 0, {}); vkCmdBindVertexBuffers(commandBuffer, 0, 1, &UniformBuffers[CURRENT_FRAME], offsets); vkCmdBindIndexBuffer(commandBuffer, binder->indexBuffer, binder->indexOffsets, VK_INDEX_TYPE_UINT16); vkCmdBindVertexBuffers(commandBuffer, binder->vertexFirstBinding, binder->vertexBindingCount, &binder->vertexBuffer, &binder->vertexOffsets); vkCmdBindVertexBuffers(commandBuffer, binder->normalsFirstBinding, binder->normalsBindingCount, &binder->normalsBuffer, &binder->normalsOffsets); vkCmdBindVertexBuffers(commandBuffer, binder->uvFirstBinding, binder->uvBindingCount, &binder->uvBuffer, &binder->uvOffsets); vkCmdBindVertexBuffers(commandBuffer, binder->instanceFirstBinding, binder->instanceBindingCount, &binder->instanceBuffer, &binder->instanceOffsets); vkCmdDrawIndexed(commandBuffer, binder->indexCount, binder->instances.Count(), 0, 0, 0); } } // present pass vkCmdEndRenderPass(commandBuffer); renderPassInfo.renderPass = presentRenderPass; renderPassInfo.framebuffer = swapchainFramebuffers[imageIndex]; renderPassInfo.clearValueCount = 1; vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &presentDescriptorSets[imageIndex], 0, nullptr); vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline); vkCmdSetViewport(commandBuffer, 0, 1, &viewport); vkCmdSetScissor(commandBuffer, 0, 1, &scissor); // reminder that present.vert is a triangle vkCmdDraw(commandBuffer, 3, 1, 0, 0); // ImGui if (isImGuiRenderActive) { ImGui_ImplVulkan_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); Event_Dispatch("RenderImGui"); ImGui::Render(); auto drawData = ImGui::GetDrawData(); const bool isMinimized = drawData->DisplaySize.x <= 0.0f || drawData->DisplaySize.y <= 0.0f; if (isMinimized) return; ImGui_ImplVulkan_RenderDrawData(drawData, commandBuffer); } vkCmdEndRenderPass(commandBuffer); result = vkEndCommandBuffer(commandBuffer); if (result != VK_SUCCESS) { throw "failed to record command buffer!"; } } void DestroySwapchain() { if (renderImageFramebuffers != nullptr && renderImageFramebuffers != CAFE_BABE(VkFramebuffer)) { for (long i = 0; i < swapchainLength; ++i) { vkDestroyFramebuffer(vkDevice, renderImageFramebuffers[i], vkAllocator); } Pke_Delete(renderImageFramebuffers, swapchainLength); } if (swapchainFramebuffers != nullptr && swapchainFramebuffers != CAFE_BABE(VkFramebuffer)) { for (long i = 0; i < swapchainLength; ++i) { vkDestroyFramebuffer(vkDevice, swapchainFramebuffers[i], vkAllocator); } Pke_Delete(swapchainFramebuffers, swapchainLength); } if (renderImageViews!= nullptr && renderImageViews != CAFE_BABE(VkImageView)) { for (long i = 0; i < swapchainLength; ++i) { vkDestroyImageView(vkDevice, depthImageViews[i], vkAllocator); vkDestroyImage(vkDevice, depthImages[i], vkAllocator); vkDestroyImageView(vkDevice, colorImageViews[i], vkAllocator); vkDestroyImage(vkDevice, colorImages[i], vkAllocator); vkDestroyImageView(vkDevice, renderImageViews[i], vkAllocator); vkDestroyImage(vkDevice, renderImages[i], vkAllocator); } Pke_Delete(depthImageViews, swapchainLength); Pke_Delete(depthImages, swapchainLength); Pke_Delete(colorImageViews, swapchainLength); Pke_Delete(colorImages, swapchainLength); Pke_Delete(renderImageViews, swapchainLength); Pke_Delete(renderImages, swapchainLength); vkFreeMemory(vkDevice, depthImagesMemory, vkAllocator); vkFreeMemory(vkDevice, colorImagesMemory, vkAllocator); vkFreeMemory(vkDevice, renderImagesMemory, vkAllocator); } if (swapchainImageViews!= nullptr && swapchainImageViews != CAFE_BABE(VkImageView)) { for (long i = 0; i < swapchainLength; ++i) { vkDestroyImageView(vkDevice, swapchainImageViews[i], vkAllocator); } Pke_Delete(swapchainImageViews, swapchainLength); Pke_Delete(swapchainImages, swapchainLength); } vkDestroySwapchainKHR(vkDevice, vkSwapchainKHR, vkAllocator); swapchainImages = CAFE_BABE(VkImage); swapchainImageViews = CAFE_BABE(VkImageView); swapchainFramebuffers = CAFE_BABE(VkFramebuffer); renderImages = CAFE_BABE(VkImage); renderImageViews = CAFE_BABE(VkImageView); renderImageFramebuffers = CAFE_BABE(VkFramebuffer); } void RecreateSwapchain() { int width, height = 0; glfwGetFramebufferSize(window, &width, &height); while (width == 0 || height == 0) { glfwGetFramebufferSize(window, &width, &height); glfwWaitEvents(); } extent.width = width; extent.height = height; vkDeviceWaitIdle(vkDevice); DestroySwapchain(); CreateSwapchain(); UpdatePresentDescriptorSets(); UpdateCameraProjection(); CreateFramebuffers(); shouldRecreateSwapchain = false; } void FramebufferResizeCallback(GLFWwindow *window, int width, int height) { if (extent.width == width && extent.height != height) { return; } shouldRecreateSwapchain = true; } void CreateWindow(PKEWindowProperties *wp) { if (vkInstance != nullptr) return; glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); window = glfwCreateWindow(wp->width, wp->height, "Pikul", nullptr, nullptr); InitVulkan(); glfwSetFramebufferSizeCallback(window, FramebufferResizeCallback); CreateSwapchain(); CreateRenderPass(); CreateGraphicsPipeline(); UpdatePresentDescriptorSets(); UpdateCameraProjection(); CreateFramebuffers(); CreateCommandPool(); CreateCommandBuffer(); CreateUniformBuffers(); CreateSyncObjects(); CreateImGui(); } void DestroyWindow() { if (vkInstance == nullptr) return; ImGui_ImplVulkan_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); DestroySwapchain(); for (long i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i) { vkDestroyBuffer(vkDevice, UniformBuffers[i], vkAllocator); vkDestroySemaphore(vkDevice, presentImageAvailableSemaphores[i], vkAllocator); vkDestroySemaphore(vkDevice, presentRenderFinishedSemaphores[i], vkAllocator); vkDestroyFence(vkDevice, presentInFlightFences[i], vkAllocator); } vkFreeMemory(vkDevice, uniformBufferMemory, vkAllocator); vkDestroyCommandPool(vkDevice, graphicsCommandPool, vkAllocator); vkDestroyCommandPool(vkDevice, transferCommandPool, vkAllocator); vkDestroyPipeline(vkDevice, graphicsPipeline, vkAllocator); vkDestroyPipelineLayout(vkDevice, pipelineLayout, vkAllocator); vkFreeDescriptorSets(vkDevice, presentDescriptorPool, MAX_FRAMES_IN_FLIGHT, presentDescriptorSets); vkDestroyDescriptorPool(vkDevice, presentDescriptorPool, vkAllocator); vkDestroyDescriptorPool(vkDevice, imGuiDescriptorPool, vkAllocator); vkDestroyDescriptorSetLayout(vkDevice, vkDescriptorSetLayout, vkAllocator); vkDestroyRenderPass(vkDevice, renderRenderPass, vkAllocator); vkDestroyRenderPass(vkDevice, presentRenderPass, vkAllocator); vkDestroySurfaceKHR(vkInstance, vkSurfaceKHR, vkAllocator); vkDestroySampler(vkDevice, presentSampler, vkAllocator); vkDestroyDevice(vkDevice, vkAllocator); if (VULKAN_DEBUG_REPORT) { auto vkDestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(vkInstance, "vkDestroyDebugReportCallbackEXT"); vkDestroyDebugReportCallbackEXT(vkInstance, vkDebugReport, vkAllocator); } vkDestroyInstance(vkInstance, vkAllocator); glfwDestroyWindow(window); glfwTerminate(); } VkShaderModule UploadShader(AssetHandle handle) { const Asset *asset = AM_Get(handle); VkShaderModuleCreateInfo createInfo; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.pNext = nullptr; createInfo.flags = {}; createInfo.codeSize = asset->size; createInfo.pCode = static_cast(asset->ptr); VkShaderModule vkShaderModule; if (vkCreateShaderModule(vkDevice, &createInfo, vkAllocator, &vkShaderModule) != VK_SUCCESS) { throw "failed to create shader module for asset"; } return vkShaderModule; } void Render() { vkWaitForFences(vkDevice, 1, &presentInFlightFences[CURRENT_FRAME], VK_TRUE, UINT64_MAX); uint32_t imageIndex; auto result = vkAcquireNextImageKHR(vkDevice, vkSwapchainKHR, UINT64_MAX, presentImageAvailableSemaphores[CURRENT_FRAME], VK_NULL_HANDLE, &imageIndex); if (result == VK_ERROR_OUT_OF_DATE_KHR) { RecreateSwapchain(); return; } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { throw "failed to acquire swapchain"; } // update uniform buffer { void *mappedUboMemory; vkMapMemory(vkDevice, uniformBufferMemory, sizeof(UniformBufferObject) * imageIndex, sizeof(UniformBufferObject), 0, &mappedUboMemory); memcpy(mappedUboMemory, &UBO, sizeof(UniformBufferObject)); vkUnmapMemory(vkDevice, uniformBufferMemory); } vkResetFences(vkDevice, 1, &presentInFlightFences[CURRENT_FRAME]); vkResetCommandBuffer(presentCommandBuffers[CURRENT_FRAME], 0); RecordCommandBuffer(presentCommandBuffers[CURRENT_FRAME], imageIndex); VkSemaphore waitSemaphores[] = {presentImageAvailableSemaphores[CURRENT_FRAME]}; VkSemaphore signalSemaphores[] = {presentRenderFinishedSemaphores[CURRENT_FRAME]}; VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; VkSubmitInfo submitInfo; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pNext = nullptr; submitInfo.waitSemaphoreCount = 1; submitInfo.pWaitSemaphores = waitSemaphores; submitInfo.pWaitDstStageMask = waitStages; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &presentCommandBuffers[CURRENT_FRAME]; submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = signalSemaphores; result = vkQueueSubmit(graphicsQueue, 1, &submitInfo, presentInFlightFences[CURRENT_FRAME]); if (result != VK_SUCCESS) { throw "failed to submit queue"; } VkSwapchainKHR swapchains[] = {vkSwapchainKHR}; VkPresentInfoKHR presentInfo; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.pNext = nullptr; presentInfo.waitSemaphoreCount = 1; presentInfo.pWaitSemaphores = signalSemaphores; presentInfo.swapchainCount = 1; presentInfo.pSwapchains = swapchains; presentInfo.pImageIndices = &imageIndex; presentInfo.pResults = nullptr; result = vkQueuePresentKHR(presentQueue, &presentInfo); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || shouldRecreateSwapchain) { RecreateSwapchain(); } else if (result != VK_SUCCESS) { throw "failed to present swapchain image"; } CURRENT_FRAME = (CURRENT_FRAME + 1) % MAX_FRAMES_IN_FLIGHT; }