7#include <vulkan/vulkan.h>
9#include <GLFW/glfw3native.h>
17#include <unordered_map>
31 const bool enableValidationLayers =
false;
33 const bool enableValidationLayers =
true;
37 const std::vector<const char*> deviceExtentions = {
38 VK_KHR_SWAPCHAIN_EXTENSION_NAME
41 DISCRETE_GPU_SCORE_BOOST = 1000,
42 MINIMUM_MAX_PUSH_CONSTANTS_SIZE = 64,
43 MINIMUM_MAX_SAMPLER_ALLOCATION_COUNT = 4000,
44 MINIMUM_MAX_IMAGE_DIMENSION_2D = 4090;
45 constexpr u16 MAX_FRAMES_IN_FLIGHT = 3;
48 u32Vec2 renderResolution { 0, 0 };
49 u32Vec2 windowResolution { 0, 0 };
50 GLFWwindow* glfwWindow;
51 bool isInitialised =
false;
53 VkPhysicalDevice physicalDevice;
55 VkQueue graphicsQueue;
56 VkDebugUtilsMessengerEXT debugMessenger;
59 VkSwapchainKHR swapChain;
60 std::vector<VkImage> swapChainImages;
61 VkFormat swapChainImageFormat;
62 VkExtent2D swapChainExtent;
63 std::vector<VkImageView> swapChainImageViews;
64 VkPipelineLayout pipelineLayout;
65 VkPipeline graphicsPipeline;
66 VkRenderPass renderPass;
67 std::vector<VkFramebuffer> swapChainFramebuffers;
68 VkCommandPool commandPool;
69 std::vector<VkCommandBuffer> commandBuffers;
72 VkSemaphore imageAvailableSemaphore;
73 VkSemaphore renderFinishedSemaphore;
74 VkFence inFlightFence;
76 std::vector<Frame> frames;
79 void CreateInstance();
80 void SetupDebugMessenger();
81 void PickPhysicalDevice();
82 void CreateLogicalDevice();
83 void CreateSwapChain();
84 void CreateImageViews();
85 void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger,
const VkAllocationCallbacks* pAllocator);
87 void DebugDeviceDetails();
88 void ClampSetResolution(u32 *resolution,
const u32 maxResolution);
89 void Error(
const char* msg);
90 static std::vector<char> ReadFile(
const std::string& filename);
91 void CreateGraphicsPipeline();
92 void CreateRenderPass();
93 void CreateFramebuffers();
94 void CreateCommandPool();
95 void CreateCommandBuffers();
97 void CreateSyncObjects();
102 Defer(std::function<
void()> Function) : DeferredFunction(Function) {}
105 if (DeferredFunction) DeferredFunction();
108 std::function<void()> DeferredFunction;
119 if (!isInitialised) Error(
"Winow not initialised - call Window::Init() first");
120 if (enableValidationLayers) std::cout <<
"Main loop has started\n";
123 while(!glfwWindowShouldClose(glfwWindow))
126 if (internal::isUpdateFunctionInitialised) internal::UpdateFunction();
130 vkDeviceWaitIdle(device);
142 if (isInitialised) Error(
"Winow already initialised - remove secondary call Window::Init()");
145 if (windowResolution.x < 1 || windowResolution.y < 1 ||
146 renderResolution.x < 1 || renderResolution.y < 1) Error(
"window and render resolution has to be set before initialisation - call Window::SetWindowResolution(u32 width, u32 height) and Window::SetRenderResolution(u32 width, u32 height) or directly set Window::windowResolution and Window::renderResolution values first");
150 glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
154 glfwWindow = glfwCreateWindow(windowResolution.x, windowResolution.y,
title,
nullptr,
nullptr);
155 if (!glfwWindow) Error(
"GLFW failed to create window");
159 SetupDebugMessenger();
161 PickPhysicalDevice();
162 CreateLogicalDevice();
166 CreateGraphicsPipeline();
167 CreateFramebuffers();
169 CreateCommandBuffers();
173 isInitialised =
true;
176 if (enableValidationLayers)
178 DebugDeviceDetails();
179 std::cout <<
"Window is initialised\n";
184 if (!isInitialised)
return;
187 if (enableValidationLayers) DestroyDebugUtilsMessengerEXT(instance, debugMessenger,
nullptr);
190 for (
size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
192 if (frames[i].imageAvailableSemaphore) vkDestroySemaphore(device, frames[i].imageAvailableSemaphore,
nullptr);
193 if (frames[i].renderFinishedSemaphore) vkDestroySemaphore(device, frames[i].renderFinishedSemaphore,
nullptr);
194 if (frames[i].inFlightFence) vkDestroyFence(device, frames[i].inFlightFence,
nullptr);
197 if (commandPool) vkDestroyCommandPool(device, commandPool,
nullptr);
199 for (
auto frambuffer : swapChainFramebuffers) vkDestroyFramebuffer(device, frambuffer,
nullptr);
201 if (graphicsPipeline) vkDestroyPipeline(device, graphicsPipeline,
nullptr);
202 if (pipelineLayout) vkDestroyPipelineLayout(device, pipelineLayout,
nullptr);
203 if (renderPass) vkDestroyRenderPass(device, renderPass,
nullptr);
205 for (
auto imageView : swapChainImageViews) vkDestroyImageView(device, imageView,
nullptr);
206 if (swapChain) vkDestroySwapchainKHR(device, swapChain,
nullptr);
207 if (device) vkDestroyDevice(device,
nullptr);
208 if (surface) vkDestroySurfaceKHR(instance, surface,
nullptr);
209 if (instance) vkDestroyInstance(instance,
nullptr);
212 if (glfwWindow) glfwDestroyWindow(glfwWindow);
214 isInitialised =
false;
215 if (enableValidationLayers) std::cout <<
"Window has been closed\n";
219 ClampSetResolution(&width, INT_MAX);
220 ClampSetResolution(&height, INT_MAX);
222 windowResolution.x = width;
223 windowResolution.y = height;
232 ClampSetResolution(&width, INT_MAX);
233 ClampSetResolution(&height, INT_MAX);
235 renderResolution.x = width;
236 renderResolution.y = height;
249 const std::vector<const char*> validationLayers = {
250 "VK_LAYER_KHRONOS_validation"
253 static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData)
255 std::cerr <<
"validation layer: " << pCallbackData->pMessage <<
"\n";
259 bool CheckValidationLayerSupport()
262 if (vkEnumerateInstanceLayerProperties(&layerCount,
nullptr) != VK_SUCCESS) Error(
"No validation layer properties, try running not in debug mode");
264 std::vector<VkLayerProperties> availableLayers(layerCount);
265 if (vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()) != VK_SUCCESS) Error(
"No validation layer layers, try running not in debug mode");
267 for (
const char* layerName : validationLayers)
269 bool layerFound =
false;
271 for (
const auto& layerProperties : availableLayers)
272 if (strcmp(layerName, layerProperties.layerName) == 0)
278 if (!layerFound)
return false;
283 VkResult CreateDebugUtilsMessengerEXT(VkInstance instance,
const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger)
285 auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance,
"vkCreateDebugUtilsMessengerEXT");
286 if (func !=
nullptr)
return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
287 else return VK_ERROR_EXTENSION_NOT_PRESENT;
289 void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger,
const VkAllocationCallbacks* pAllocator)
291 auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance,
"vkDestroyDebugUtilsMessengerEXT");
292 if (func !=
nullptr) func(instance, debugMessenger, pAllocator);
294 void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo)
296 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
297 createInfo.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;
298 createInfo.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;
299 createInfo.pfnUserCallback = DebugCallback;
301 void SetupDebugMessenger()
303 if (!enableValidationLayers)
return;
305 VkDebugUtilsMessengerCreateInfoEXT createInfo {};
306 populateDebugMessengerCreateInfo(createInfo);
308 if (CreateDebugUtilsMessengerEXT(instance, &createInfo,
nullptr, &debugMessenger) != VK_SUCCESS) Error(
"failed to set up debug messenger!");
310 std::vector<const char*> GetRequiredExtensions()
312 u32 glfwExtensionCount = 0;
313 const char** glfwExtensions;
314 glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
316 std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
318 if (enableValidationLayers) extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
326 void CreateInstance()
329 if (enableValidationLayers && !CheckValidationLayerSupport()) Error(
"DEBUG: validation layers are not available");
332 VkApplicationInfo appInfo {};
333 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
335 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
338 appInfo.apiVersion = VK_API_VERSION_1_0;
341 VkInstanceCreateInfo createInfo {};
342 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
343 createInfo.pApplicationInfo = &appInfo;
345 auto glfwExtensions = GetRequiredExtensions();
347 createInfo.enabledExtensionCount =
static_cast<u32
>(glfwExtensions.size());
348 createInfo.ppEnabledExtensionNames = glfwExtensions.data();
350 if (enableValidationLayers)
352 createInfo.enabledLayerCount =
static_cast<u32
>(validationLayers.size());
353 createInfo.ppEnabledLayerNames = validationLayers.data();
355 else createInfo.enabledLayerCount = 0;
359 if (vkCreateInstance(&createInfo,
nullptr, &instance) != VK_SUCCESS) Error(
"failed to create Vulkan instance :(");
362 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
363 if (enableValidationLayers)
365 createInfo.enabledLayerCount =
static_cast<u32
>(validationLayers.size());
366 createInfo.ppEnabledLayerNames = validationLayers.data();
368 populateDebugMessengerCreateInfo(debugCreateInfo);
369 createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &debugCreateInfo;
373 createInfo.enabledLayerCount = 0;
374 createInfo.pNext =
nullptr;
381 struct SwapChainSupportDetails {
382 VkSurfaceCapabilitiesKHR capabilities;
383 std::vector<VkSurfaceFormatKHR> formats;
384 std::vector<VkPresentModeKHR> presentModes;
386 SwapChainSupportDetails QuerySwapChainSupport(VkPhysicalDevice device)
388 SwapChainSupportDetails details;
389 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);
391 vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount,
nullptr);
393 if (formatCount != 0) {
394 details.formats.resize(formatCount);
395 vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
397 u32 presentModeCount;
398 vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount,
nullptr);
400 if (presentModeCount != 0) {
401 details.presentModes.resize(presentModeCount);
402 vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());
407 VkSurfaceFormatKHR ChooseSwapSurfaceFormat(
const std::vector<VkSurfaceFormatKHR>& availableFormats) {
408 for (
const auto& availableFormat : availableFormats)
410 if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
412 return availableFormat;
416 return availableFormats[0];
418 VkPresentModeKHR ChooseSwapPresentMode(
const std::vector<VkPresentModeKHR>& availablePresentModes)
420 for (
const auto& availablePresentMode : availablePresentModes) {
421 if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR)
return availablePresentMode;
424 return VK_PRESENT_MODE_FIFO_KHR;
426 VkExtent2D ChooseSwapExtent(
const VkSurfaceCapabilitiesKHR& capabilities)
428 if (capabilities.currentExtent.width != std::numeric_limits<u32>::max())
return capabilities.currentExtent;
432 glfwGetFramebufferSize(glfwWindow, &width, &height);
434 VkExtent2D actualExtent = {
435 static_cast<u32
>(width),
436 static_cast<u32
>(height)
439 actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
440 actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
445 struct QueueFamilyIndices
447 std::optional<u32> graphicsFamily;
448 std::optional<u32> presentFamily;
452 return graphicsFamily.has_value() && presentFamily.has_value();
455 QueueFamilyIndices FindQueueFamilies(VkPhysicalDevice device)
457 QueueFamilyIndices indices {};
459 u32 queueFamilyCount = 0;
460 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount,
nullptr);
461 std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
462 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
465 for (
const auto& queueFamily : queueFamilies)
468 if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) indices.graphicsFamily = i;
471 VkBool32 presentSupport =
false;
472 vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
473 if (presentSupport) indices.presentFamily = i;
475 if (indices.isComplete())
break;;
480 void CreateSwapChain()
482 SwapChainSupportDetails swapChainSupport = QuerySwapChainSupport(physicalDevice);
484 VkSurfaceFormatKHR surfaceFormat = ChooseSwapSurfaceFormat(swapChainSupport.formats);
485 VkPresentModeKHR presentMode = ChooseSwapPresentMode(swapChainSupport.presentModes);
486 VkExtent2D extent = ChooseSwapExtent(swapChainSupport.capabilities);
488 u32 imageCount = swapChainSupport.capabilities.minImageCount + 1;
489 if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount)
490 imageCount = swapChainSupport.capabilities.maxImageCount;
492 VkSwapchainCreateInfoKHR createInfo{};
493 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
494 createInfo.surface = surface;
495 createInfo.minImageCount = imageCount;
496 createInfo.imageFormat = surfaceFormat.format;
497 createInfo.imageColorSpace = surfaceFormat.colorSpace;
498 createInfo.imageExtent = extent;
499 createInfo.imageArrayLayers = 1;
500 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
502 QueueFamilyIndices indices = FindQueueFamilies(physicalDevice);
503 u32 queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()};
505 if (indices.graphicsFamily != indices.presentFamily)
507 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
508 createInfo.queueFamilyIndexCount = 2;
509 createInfo.pQueueFamilyIndices = queueFamilyIndices;
513 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
514 createInfo.queueFamilyIndexCount = 0;
515 createInfo.pQueueFamilyIndices =
nullptr;
517 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
518 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
520 createInfo.presentMode = presentMode;
521 createInfo.clipped = VK_TRUE;
522 createInfo.oldSwapchain = VK_NULL_HANDLE;
524 if (vkCreateSwapchainKHR(device, &createInfo,
nullptr, &swapChain) != VK_SUCCESS) Error(
"Failed to create swap chain");
526 vkGetSwapchainImagesKHR(device, swapChain, &imageCount,
nullptr);
527 swapChainImages.resize(imageCount);
528 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
530 swapChainImageFormat = surfaceFormat.format;
531 swapChainExtent = extent;
537 void CreateImageViews()
539 swapChainImageViews.resize(swapChainImages.size());
540 for (
size_t i = 0; i < swapChainImages.size(); i++)
542 VkImageViewCreateInfo createInfo {};
543 createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
544 createInfo.image = swapChainImages[i];
545 createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
546 createInfo.format = swapChainImageFormat;
547 createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
548 createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
549 createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
550 createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
551 createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
552 createInfo.subresourceRange.baseMipLevel = 0;
553 createInfo.subresourceRange.levelCount = 1;
554 createInfo.subresourceRange.baseArrayLayer = 0;
555 createInfo.subresourceRange.layerCount = 1;
557 if (vkCreateImageView(device, &createInfo,
nullptr, &swapChainImageViews[i]) != VK_SUCCESS) Error(
"Failed to create image views");
564 bool CheckDeviceExtensionSupport(VkPhysicalDevice device)
567 vkEnumerateDeviceExtensionProperties(device,
nullptr, &extensionCount,
nullptr);
569 std::vector<VkExtensionProperties> availableExtensions(extensionCount);
570 vkEnumerateDeviceExtensionProperties(device,
nullptr, &extensionCount, availableExtensions.data());
572 std::set<std::string> requiredExtentions(deviceExtentions.begin(), deviceExtentions.end());
574 for (
const auto& extension : availableExtensions) requiredExtentions.erase(extension.extensionName);
575 return requiredExtentions.empty();
577 std::pair<u32, VkPhysicalDevice> RateDeviceSuitability(VkPhysicalDevice device)
579 VkPhysicalDeviceProperties properties;
580 vkGetPhysicalDeviceProperties(device, &properties);
581 VkPhysicalDeviceFeatures features;
582 vkGetPhysicalDeviceFeatures(device, &features);
585 const std::pair BELOW_MINIMUM_SPEC = std::make_pair((u32)0, device);
588 if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) score += DISCRETE_GPU_SCORE_BOOST;
591 score += properties.limits.maxImageDimension2D;
594 if (enableValidationLayers)
596 VkPhysicalDeviceProperties deviceProperties;
597 vkGetPhysicalDeviceProperties(device, &deviceProperties);
598 std::cout <<
"Device and its score: " << deviceProperties.deviceName <<
" : " << score <<
'\n';
602 if (!CheckDeviceExtensionSupport(device))
return BELOW_MINIMUM_SPEC;
603 SwapChainSupportDetails swapChainSupport = QuerySwapChainSupport(device);
604 if (swapChainSupport.formats.empty() || swapChainSupport.presentModes.empty())
return BELOW_MINIMUM_SPEC;
609 !features.geometryShader ||
611 properties.limits.maxPushConstantsSize < MINIMUM_MAX_PUSH_CONSTANTS_SIZE ||
612 properties.limits.maxSamplerAllocationCount < MINIMUM_MAX_SAMPLER_ALLOCATION_COUNT ||
613 properties.limits.maxImageDimension2D < MINIMUM_MAX_IMAGE_DIMENSION_2D
614 )
return BELOW_MINIMUM_SPEC;
616 if (!FindQueueFamilies(device).graphicsFamily.has_value())
return BELOW_MINIMUM_SPEC;
618 return std::make_pair(score, device);
620 void PickPhysicalDevice()
623 vkEnumeratePhysicalDevices(instance, &deviceCount,
nullptr);
625 if (deviceCount == 0) Error(
"Couldn't find any GPU with Vulkan support :(");
627 std::vector<VkPhysicalDevice> devices(deviceCount);
628 vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
630 std::vector<std::pair<u32, VkPhysicalDevice>> candidates;
633 for (
const auto& device : devices) candidates.push_back(RateDeviceSuitability(device));
635 auto selectedDevice = std::max_element(
638 [](
const auto& a,
const auto& b)
640 return a.first < b.first;
643 if ((*selectedDevice).first == 0) Error(
"Couldn't find suitable GPU :(");
645 physicalDevice = (*selectedDevice).second;
651 void CreateLogicalDevice()
653 QueueFamilyIndices indices = FindQueueFamilies(physicalDevice);
655 std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
656 std::set<u32> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
658 float queuePriority = 1.0f;
659 for (u32 queueFamily : uniqueQueueFamilies) {
660 VkDeviceQueueCreateInfo queueCreateInfo{};
661 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
662 queueCreateInfo.queueFamilyIndex = queueFamily;
663 queueCreateInfo.queueCount = 1;
664 queueCreateInfo.pQueuePriorities = &queuePriority;
665 queueCreateInfos.push_back(queueCreateInfo);
668 VkDeviceQueueCreateInfo queueCreateInfo{};
669 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
670 queueCreateInfo.queueFamilyIndex = indices.graphicsFamily.value();
671 queueCreateInfo.queueCount = 1;
673 queueCreateInfo.pQueuePriorities = &queuePriority;
675 VkPhysicalDeviceFeatures deviceFeatures{};
677 VkDeviceCreateInfo createInfo{};
678 createInfo.queueCreateInfoCount =
static_cast<u32
>(queueCreateInfos.size());
679 createInfo.pQueueCreateInfos = queueCreateInfos.data();
681 createInfo.pEnabledFeatures = &deviceFeatures;
683 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
684 createInfo.pQueueCreateInfos = &queueCreateInfo;
685 createInfo.queueCreateInfoCount = 1;
687 createInfo.enabledExtensionCount =
static_cast<u32
>(deviceExtentions.size());
688 createInfo.ppEnabledExtensionNames = deviceExtentions.data();
690 if (vkCreateDevice(physicalDevice, &createInfo,
nullptr, &device) != VK_SUCCESS) Error(
"failed to create logical device");
691 vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
692 vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
701 if (glfwCreateWindowSurface(instance, glfwWindow, NULL, &surface) != VK_SUCCESS)
703 Error(
"Failed to create window surface");
710 VkShaderModule CreateShaderModule(
const std::vector<char>& code)
712 VkShaderModuleCreateInfo createInfo {};
713 createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
714 createInfo.codeSize = code.size();
715 createInfo.pCode =
reinterpret_cast<const u32*
>(code.data());
717 VkShaderModule shaderModule;
718 if (vkCreateShaderModule(device, &createInfo,
nullptr, &shaderModule) != VK_SUCCESS) Error(
"failed to create shader module");
721 void CreateGraphicsPipeline()
723 auto vertShaderCode = ReadFile(
"shaders/vert.spv");
724 auto fragShaderCode = ReadFile(
"shaders/frag.spv");
726 VkShaderModule vertShaderModule = CreateShaderModule(vertShaderCode);
727 VkShaderModule fragShaderModule = CreateShaderModule(fragShaderCode);
729 VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
730 vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
731 vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
732 vertShaderStageInfo.module = vertShaderModule;
733 vertShaderStageInfo.pName =
"main";
735 VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
736 fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
737 fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
738 fragShaderStageInfo.module = fragShaderModule;
739 fragShaderStageInfo.pName =
"main";
741 VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};
743 VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
744 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
745 vertexInputInfo.vertexBindingDescriptionCount = 0;
746 vertexInputInfo.vertexAttributeDescriptionCount = 0;
748 VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
749 inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
750 inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
751 inputAssembly.primitiveRestartEnable = VK_FALSE;
753 VkPipelineViewportStateCreateInfo viewportState{};
754 viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
755 viewportState.viewportCount = 1;
756 viewportState.scissorCount = 1;
758 VkPipelineRasterizationStateCreateInfo rasterizer{};
759 rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
760 rasterizer.depthClampEnable = VK_FALSE;
761 rasterizer.rasterizerDiscardEnable = VK_FALSE;
762 rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
763 rasterizer.lineWidth = 1.0f;
764 rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
765 rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
766 rasterizer.depthBiasEnable = VK_FALSE;
768 VkPipelineMultisampleStateCreateInfo multisampling{};
769 multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
770 multisampling.sampleShadingEnable = VK_FALSE;
771 multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
773 VkPipelineColorBlendAttachmentState colorBlendAttachment{};
774 colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
775 colorBlendAttachment.blendEnable = VK_FALSE;
777 VkPipelineColorBlendStateCreateInfo colorBlending{};
778 colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
779 colorBlending.logicOpEnable = VK_FALSE;
780 colorBlending.logicOp = VK_LOGIC_OP_COPY;
781 colorBlending.attachmentCount = 1;
782 colorBlending.pAttachments = &colorBlendAttachment;
783 colorBlending.blendConstants[0] = 0.0f;
784 colorBlending.blendConstants[1] = 0.0f;
785 colorBlending.blendConstants[2] = 0.0f;
786 colorBlending.blendConstants[3] = 0.0f;
788 std::vector<VkDynamicState> dynamicStates = {
789 VK_DYNAMIC_STATE_VIEWPORT,
790 VK_DYNAMIC_STATE_SCISSOR
792 VkPipelineDynamicStateCreateInfo dynamicState{};
793 dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
794 dynamicState.dynamicStateCount =
static_cast<uint32_t
>(dynamicStates.size());
795 dynamicState.pDynamicStates = dynamicStates.data();
797 VkPipelineLayoutCreateInfo pipelineLayoutInfo {};
798 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
799 pipelineLayoutInfo.setLayoutCount = 0;
800 pipelineLayoutInfo.pushConstantRangeCount = 0;
802 if (vkCreatePipelineLayout(device, &pipelineLayoutInfo,
nullptr, &pipelineLayout) != VK_SUCCESS) Error(
"failed to create pipeline layout");
804 VkGraphicsPipelineCreateInfo pipelineInfo{};
805 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
806 pipelineInfo.stageCount = 2;
807 pipelineInfo.pStages = shaderStages;
808 pipelineInfo.pVertexInputState = &vertexInputInfo;
809 pipelineInfo.pInputAssemblyState = &inputAssembly;
810 pipelineInfo.pViewportState = &viewportState;
811 pipelineInfo.pRasterizationState = &rasterizer;
812 pipelineInfo.pMultisampleState = &multisampling;
813 pipelineInfo.pColorBlendState = &colorBlending;
814 pipelineInfo.pDynamicState = &dynamicState;
815 pipelineInfo.layout = pipelineLayout;
816 pipelineInfo.renderPass = renderPass;
817 pipelineInfo.subpass = 0;
818 pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
820 if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo,
nullptr, &graphicsPipeline) != VK_SUCCESS) Error(
"failed to create graphics pipeline");
822 vkDestroyShaderModule(device, fragShaderModule,
nullptr);
823 vkDestroyShaderModule(device, vertShaderModule,
nullptr);
825 void CreateRenderPass()
827 VkAttachmentDescription colorAttachment{};
828 colorAttachment.format = swapChainImageFormat;
829 colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
830 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
831 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
832 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
833 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
834 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
835 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
837 VkAttachmentReference colorAttachmentRef{};
838 colorAttachmentRef.attachment = 0;
839 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
841 VkSubpassDescription subpass{};
842 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
843 subpass.colorAttachmentCount = 1;
844 subpass.pColorAttachments = &colorAttachmentRef;
846 VkSubpassDependency dependency{};
847 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
848 dependency.dstSubpass = 0;
849 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
850 dependency.srcAccessMask = 0;
851 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
852 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
854 VkRenderPassCreateInfo renderPassInfo{};
855 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
856 renderPassInfo.attachmentCount = 1;
857 renderPassInfo.pAttachments = &colorAttachment;
858 renderPassInfo.subpassCount = 1;
859 renderPassInfo.pSubpasses = &subpass;
860 renderPassInfo.dependencyCount = 1;
861 renderPassInfo.pDependencies = &dependency;
863 if (vkCreateRenderPass(device, &renderPassInfo,
nullptr, &renderPass) != VK_SUCCESS) Error(
"failed to create render pass");
869 void CreateFramebuffers()
871 swapChainFramebuffers.resize(swapChainImageViews.size());
873 for (
size_t i = 0; i < swapChainImageViews.size(); i++) {
874 VkImageView attachments[] = {
875 swapChainImageViews[i]
878 VkFramebufferCreateInfo framebufferInfo{};
879 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
880 framebufferInfo.renderPass = renderPass;
881 framebufferInfo.attachmentCount = 1;
882 framebufferInfo.pAttachments = attachments;
883 framebufferInfo.width = swapChainExtent.width;
884 framebufferInfo.height = swapChainExtent.height;
885 framebufferInfo.layers = 1;
887 if (vkCreateFramebuffer(device, &framebufferInfo,
nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) Error(
"failed to create framebuffer");
890 void CreateCommandPool()
892 QueueFamilyIndices queueFamilyIndices = FindQueueFamilies(physicalDevice);
894 VkCommandPoolCreateInfo poolInfo{};
895 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
896 poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
897 poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
899 if (vkCreateCommandPool(device, &poolInfo,
nullptr, &commandPool) != VK_SUCCESS) Error(
"Failed to create command pool");
901 void CreateCommandBuffers()
903 commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
905 VkCommandBufferAllocateInfo allocInfo {};
906 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
907 allocInfo.commandPool = commandPool;
908 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
909 allocInfo.commandBufferCount = (u32)commandBuffers.size();
911 if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) Error(
"failed to allocate command buffers");
913 VkClearColorValue ConvertBackgroundColour()
922 void RecordCommandBuffer(VkCommandBuffer commandBuffer, u32 imageIndex)
924 VkCommandBufferBeginInfo beginInfo{};
925 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
927 beginInfo.pInheritanceInfo =
nullptr;
929 if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) Error(
"failed to begin recording command buffer");
931 VkRenderPassBeginInfo renderPassInfo{};
932 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
933 renderPassInfo.renderPass = renderPass;
934 renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex];
935 renderPassInfo.renderArea.offset = {0, 0};
936 renderPassInfo.renderArea.extent = swapChainExtent;
937 VkClearValue clearColor = { ConvertBackgroundColour() };
938 renderPassInfo.clearValueCount = 1;
939 renderPassInfo.pClearValues = &clearColor;
940 vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
942 vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
943 VkViewport viewport{};
946 viewport.width =
static_cast<float>(swapChainExtent.width);
947 viewport.height =
static_cast<float>(swapChainExtent.height);
948 viewport.minDepth = 0.0f;
949 viewport.maxDepth = 1.0f;
950 vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
953 scissor.offset = {0, 0};
954 scissor.extent = swapChainExtent;
955 vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
958 vkCmdDraw(commandBuffer, 3, 1, 0, 0);
960 vkCmdEndRenderPass(commandBuffer);
961 if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) Error(
"failed to record command buffer");
967 void CreateSyncObjects()
969 frames.resize(MAX_FRAMES_IN_FLIGHT);
971 VkSemaphoreCreateInfo semaphoreInfo {};
972 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
974 VkFenceCreateInfo fenceInfo {};
975 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
976 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
978 for (
size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
979 if (vkCreateSemaphore(device, &semaphoreInfo,
nullptr, &frames[i].imageAvailableSemaphore) != VK_SUCCESS
980 || vkCreateSemaphore(device, &semaphoreInfo,
nullptr, &frames[i].renderFinishedSemaphore) != VK_SUCCESS
981 || vkCreateFence(device, &fenceInfo,
nullptr, &frames[i].inFlightFence) != VK_SUCCESS) Error(
"failed to create semaphores");
985 vkWaitForFences(device, 1, &frames[currentFrame].inFlightFence, VK_TRUE, UINT64_MAX);
986 vkResetFences(device, 1, &frames[currentFrame].inFlightFence);
989 vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, frames[currentFrame].imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);
991 vkResetCommandBuffer(commandBuffers[currentFrame], 0);
992 RecordCommandBuffer(commandBuffers[currentFrame], imageIndex);
994 VkSubmitInfo submitInfo {};
995 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
997 VkSemaphore waitSemaphores[] = { frames[currentFrame].imageAvailableSemaphore };
998 VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
999 submitInfo.waitSemaphoreCount = 1;
1000 submitInfo.pWaitSemaphores = waitSemaphores;
1001 submitInfo.pWaitDstStageMask = waitStages;
1002 submitInfo.commandBufferCount = 1;
1003 submitInfo.pCommandBuffers = &commandBuffers[currentFrame];
1005 VkSemaphore signalSemaphores[] = { frames[currentFrame].renderFinishedSemaphore };
1006 submitInfo.signalSemaphoreCount = 1;
1007 submitInfo.pSignalSemaphores = signalSemaphores;
1009 if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, frames[currentFrame].inFlightFence) != VK_SUCCESS) Error(
"failed to submit draw command buffer");
1011 VkSubpassDependency dependency{};
1012 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
1013 dependency.dstSubpass = 0;
1014 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1015 dependency.srcAccessMask = 0;
1016 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1017 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
1019 VkPresentInfoKHR presentInfo{};
1020 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
1022 presentInfo.waitSemaphoreCount = 1;
1023 presentInfo.pWaitSemaphores = signalSemaphores;
1025 VkSwapchainKHR swapChains[] = { swapChain };
1026 presentInfo.swapchainCount = 1;
1027 presentInfo.pSwapchains = swapChains;
1028 presentInfo.pImageIndices = &imageIndex;
1029 presentInfo.pResults =
nullptr;
1031 vkQueuePresentKHR(presentQueue, &presentInfo);
1032 currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
1038 void DebugDeviceDetails()
1040 VkPhysicalDeviceProperties deviceProperties;
1041 vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
1043 static const std::unordered_map<u32, const char*> gpuVendors = {
1050 static const u8 LABEL_WIDTH = 15,
1052 auto PrintDetail = [](
const char* label,
const auto& value)
1054 std::cout << std::setw(LABEL_WIDTH) << std::left << label <<
": "
1055 << std::setw(VALUE_WIDTH) << std::left << value <<
"\n";
1058 PrintDetail(
"GPU selected", deviceProperties.deviceName);
1059 PrintDetail(
"API version", std::to_string(deviceProperties.apiVersion) +
" (" +
1060 std::to_string(VK_VERSION_MAJOR(deviceProperties.apiVersion)) +
"." +
1061 std::to_string(VK_VERSION_MINOR(deviceProperties.apiVersion)) +
"." +
1062 std::to_string(VK_VERSION_PATCH(deviceProperties.apiVersion)) +
")");
1064 PrintDetail(
"Driver version", deviceProperties.driverVersion);
1065 PrintDetail(
"Device ID", deviceProperties.deviceID);
1067 u32 vendorID = deviceProperties.vendorID;
1068 std::string vendorName = gpuVendors.count(vendorID) ? gpuVendors.at(vendorID) :
"Unknown";
1069 PrintDetail(
"Vendor ID", std::to_string(vendorID) +
" (" + vendorName +
")");
1071 void ClampSetResolution(u32 *resolution,
const u32 maxResolution)
1074 *resolution = *resolution < maxResolution ? *resolution : maxResolution;
1076 void Error(
const char* msg)
1078 if (enableValidationLayers) std::cerr <<
"Error: " << msg <<
'\n';
1080 throw std::runtime_error(msg);
1082 static std::vector<char> ReadFile(
const std::string& filename)
1085 std::ifstream file(filename, std::ios::ate | std::ios::binary);
1087 if (!file.is_open()) Error(
"failed to open file!");
1089 size_t fileSize = (size_t) file.tellg();
1090 std::vector<char> buffer(fileSize);
1093 file.read(buffer.data(), fileSize);
constexpr u16 UPDATE_PATCH
Patch update version.
constexpr u16 UPDATE_MAJOR
Major update version.
constexpr u16 UPDATE_MINOR
Minor update version.
constexpr const char * ENGINE_NAME
The name of the game engine TODO: instert name here when decided.
Handles window creation and Vulkan initialization and main loop. code can be found in window....
const char * title
Title of the window.
void SetWindowResolution(u32 width, u32 height)
Sets the window resolution.
u32Vec2 GetWindowResolution()
Gets the window resolution.
Colour backgroundColour
Background colour, has alias backgroundColor.
void SetRenderResolution(u32 width, u32 height)
Sets the render resolution.
void Close()
Closes the window and cleans up Vulkan.
bool isResizable
Going to be flags later on...
void Init()
alias for backgroundColour
u32Vec2 GetRenderResolution()
Gets the render resolution.
Has alias 'Color'. Holds an 24 bit colour (+ an alpha channel)
2D vector of 32 bit width unsigned integers
Defines window namespace, can be found in window.cpp.