Untitled 2D game engine pre-alpha
A 2D game engine made with Vulkan in C++
 
Loading...
Searching...
No Matches
window.cpp
Go to the documentation of this file.
1
4
5#include "window.h"
6#include "internal.h"
7#include <vulkan/vulkan.h>
8#include <GLFW/glfw3.h>
9#include <GLFW/glfw3native.h>
10
11// Standard Library
12#include <stdexcept>
13#include <vector>
14#include <utility>
15#include <optional>
16#include <iostream>
17#include <unordered_map>
18#include <iomanip>
19#include <string>
20#include <cstring>
21#include <algorithm>
22#include <functional>
23#include <set>
24#include <fstream>
25
26// Forward declarations so PUBLIC functions can appear first (Nicer to read)
27namespace
28{
29 // Turn validation layers on or off based on if debug build or not
30 #ifdef NDEBUG
31 const bool enableValidationLayers = false;
32 #else
33 const bool enableValidationLayers = true;
34 #endif
35
36 // Constants only needed in this file:
37 const std::vector<const char*> deviceExtentions = {
38 VK_KHR_SWAPCHAIN_EXTENSION_NAME
39 };
40 constexpr u16
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;
46
47 // Variables and functions
48 u32Vec2 renderResolution { 0, 0 };
49 u32Vec2 windowResolution { 0, 0 };
50 GLFWwindow* glfwWindow;
51 bool isInitialised = false;
52 VkInstance instance;
53 VkPhysicalDevice physicalDevice; // GPU
54 VkDevice device;
55 VkQueue graphicsQueue;
56 VkDebugUtilsMessengerEXT debugMessenger;
57 VkSurfaceKHR surface;
58 VkQueue presentQueue;
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;
70
71 struct Frame {
72 VkSemaphore imageAvailableSemaphore;
73 VkSemaphore renderFinishedSemaphore;
74 VkFence inFlightFence;
75 };
76 std::vector<Frame> frames;
77 u16 currentFrame = 0;
78
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);
86 void CreateSurface();
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();
96 void DrawFrame();
97 void CreateSyncObjects();
98
99 // To run a function at the end of this scope
100 class Defer {
101 public:
102 Defer(std::function<void()> Function) : DeferredFunction(Function) {}
103 ~Defer()
104 {
105 if (DeferredFunction) DeferredFunction();
106 }
107 private:
108 std::function<void()> DeferredFunction;
109 };
110 // DEFERED EXIT CALL
111 Defer DeferredExitCall([&](){ window::Close(); });
112}
113
114// Code makes sense to be here as it uses private window variables. though game::Run() makes more sense than window::Run()
115namespace internal
116{
117 void UpdateLoop()
118 {
119 if (!isInitialised) Error("Winow not initialised - call Window::Init() first");
120 if (enableValidationLayers) std::cout << "Main loop has started\n";
121
122 // Main loop
123 while(!glfwWindowShouldClose(glfwWindow))
124 {
125 glfwPollEvents();
126 if (internal::isUpdateFunctionInitialised) internal::UpdateFunction();
127 DrawFrame();
128 }
129
130 vkDeviceWaitIdle(device);
131 }
132}
133// PUBLIC
134namespace window
135{
136 const char* title = "...";
137 bool isResizable = false;
139
140 void Init()
141 {
142 if (isInitialised) Error("Winow already initialised - remove secondary call Window::Init()");
143
144 // Validate that the window and render size was set
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");
147
148 // GLFW
149 glfwInit();
150 glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); // Set API to vulkan
151 glfwWindowHint(GLFW_RESIZABLE, isResizable);
152
153 // CREATE WINDOW
154 glfwWindow = glfwCreateWindow(windowResolution.x, windowResolution.y, title, nullptr, nullptr);
155 if (!glfwWindow) Error("GLFW failed to create window");
156
157 // VULKAN
158 CreateInstance();
159 SetupDebugMessenger();
160 CreateSurface();
161 PickPhysicalDevice();
162 CreateLogicalDevice();
163 CreateSwapChain();
164 CreateImageViews();
165 CreateRenderPass();
166 CreateGraphicsPipeline();
167 CreateFramebuffers();
168 CreateCommandPool();
169 CreateCommandBuffers();
170 CreateSyncObjects();
171
172 // GENERAL
173 isInitialised = true;
174
175 // DEBUG OUTPUT
176 if (enableValidationLayers)
177 {
178 DebugDeviceDetails();
179 std::cout << "Window is initialised\n";
180 }
181 }
182 void Close()
183 {
184 if (!isInitialised) return; // Don't cause error when calling close multiple times
185
186 // Debug
187 if (enableValidationLayers) DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
188
189 // Vulkan cleanup
190 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
191 {
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);
195 }
196
197 if (commandPool) vkDestroyCommandPool(device, commandPool, nullptr);
198
199 for (auto frambuffer : swapChainFramebuffers) vkDestroyFramebuffer(device, frambuffer, nullptr);
200
201 if (graphicsPipeline) vkDestroyPipeline(device, graphicsPipeline, nullptr);
202 if (pipelineLayout) vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
203 if (renderPass) vkDestroyRenderPass(device, renderPass, nullptr);
204
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);
210
211 // Other
212 if (glfwWindow) glfwDestroyWindow(glfwWindow);
213 glfwTerminate();
214 isInitialised = false;
215 if (enableValidationLayers) std::cout << "Window has been closed\n";
216 }
217 void SetWindowResolution(u32 width, u32 height)
218 {
219 ClampSetResolution(&width, INT_MAX);
220 ClampSetResolution(&height, INT_MAX);
221
222 windowResolution.x = width;
223 windowResolution.y = height;
224
225 if (isInitialised)
226 {
227 // TODO: change size of window
228 }
229 }
230 void SetRenderResolution(u32 width, u32 height)
231 {
232 ClampSetResolution(&width, INT_MAX);
233 ClampSetResolution(&height, INT_MAX);
234
235 renderResolution.x = width;
236 renderResolution.y = height;
237 }
238 u32Vec2 GetRenderResolution() { return renderResolution; }
239 u32Vec2 GetWindowResolution() { return windowResolution; }
240}
241
242// PRIVATE
243// Boiler plate code based of https://vulkan-tutorial.com
244namespace
245{
246 // -------------------------------------
247 // Validation Layers
248 // -------------------------------------
249 const std::vector<const char*> validationLayers = {
250 "VK_LAYER_KHRONOS_validation"
251 };
252
253 static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData)
254 {
255 std::cerr <<"validation layer: " << pCallbackData->pMessage << "\n";
256
257 return VK_FALSE;
258 }
259 bool CheckValidationLayerSupport()
260 {
261 u32 layerCount;
262 if (vkEnumerateInstanceLayerProperties(&layerCount, nullptr) != VK_SUCCESS) Error("No validation layer properties, try running not in debug mode");
263
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");
266
267 for (const char* layerName : validationLayers)
268 {
269 bool layerFound = false;
270
271 for (const auto& layerProperties : availableLayers)
272 if (strcmp(layerName, layerProperties.layerName) == 0)
273 {
274 layerFound = true;
275 break;
276 }
277
278 if (!layerFound) return false;
279 }
280
281 return true;
282 }
283 VkResult CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger)
284 {
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;
288 }
289 void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator)
290 {
291 auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
292 if (func != nullptr) func(instance, debugMessenger, pAllocator);
293 }
294 void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo)
295 {
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;
300 }
301 void SetupDebugMessenger()
302 {
303 if (!enableValidationLayers) return;
304
305 VkDebugUtilsMessengerCreateInfoEXT createInfo {};
306 populateDebugMessengerCreateInfo(createInfo);
307
308 if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) Error("failed to set up debug messenger!");
309 }
310 std::vector<const char*> GetRequiredExtensions()
311 {
312 u32 glfwExtensionCount = 0;
313 const char** glfwExtensions;
314 glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
315
316 std::vector<const char*> extensions(glfwExtensions, glfwExtensions + glfwExtensionCount);
317
318 if (enableValidationLayers) extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
319
320 return extensions;
321 }
322
323 // -------------------------------------
324 // Create Vulkan Instance
325 // -------------------------------------
326 void CreateInstance()
327 {
328 // Debug error handling
329 if (enableValidationLayers && !CheckValidationLayerSupport()) Error("DEBUG: validation layers are not available");
330
331 // Metadata for Vulkan
332 VkApplicationInfo appInfo {};
333 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
334 appInfo.pApplicationName = window::title;
335 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); // TODO: allow game developer to set version of their game here
336 appInfo.pEngineName = ENGINE_NAME;
337 appInfo.engineVersion = VK_MAKE_VERSION(UPDATE_MAJOR, UPDATE_MINOR, UPDATE_PATCH);
338 appInfo.apiVersion = VK_API_VERSION_1_0;
339
340 // GLFW extention
341 VkInstanceCreateInfo createInfo {};
342 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
343 createInfo.pApplicationInfo = &appInfo;
344
345 auto glfwExtensions = GetRequiredExtensions();
346
347 createInfo.enabledExtensionCount = static_cast<u32>(glfwExtensions.size());
348 createInfo.ppEnabledExtensionNames = glfwExtensions.data();
349 // If debug mode is on, turn on validation layers
350 if (enableValidationLayers)
351 {
352 createInfo.enabledLayerCount = static_cast<u32>(validationLayers.size());
353 createInfo.ppEnabledLayerNames = validationLayers.data();
354 }
355 else createInfo.enabledLayerCount = 0;
356
357
358 // Create Vulkan instance itself
359 if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) Error("failed to create Vulkan instance :(");
360
361 // Validation Layers
362 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
363 if (enableValidationLayers)
364 {
365 createInfo.enabledLayerCount = static_cast<u32>(validationLayers.size());
366 createInfo.ppEnabledLayerNames = validationLayers.data();
367
368 populateDebugMessengerCreateInfo(debugCreateInfo);
369 createInfo.pNext = (VkDebugUtilsMessengerCreateInfoEXT*) &debugCreateInfo;
370 }
371 else
372 {
373 createInfo.enabledLayerCount = 0;
374 createInfo.pNext = nullptr;
375 }
376 }
377
378 // -------------------------------------
379 // Swap Chain
380 // -------------------------------------
381 struct SwapChainSupportDetails {
382 VkSurfaceCapabilitiesKHR capabilities;
383 std::vector<VkSurfaceFormatKHR> formats;
384 std::vector<VkPresentModeKHR> presentModes;
385 };
386 SwapChainSupportDetails QuerySwapChainSupport(VkPhysicalDevice device)
387 {
388 SwapChainSupportDetails details;
389 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);
390 u32 formatCount;
391 vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
392
393 if (formatCount != 0) {
394 details.formats.resize(formatCount);
395 vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
396 }
397 u32 presentModeCount;
398 vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
399
400 if (presentModeCount != 0) {
401 details.presentModes.resize(presentModeCount);
402 vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());
403 }
404
405 return details;
406 }
407 VkSurfaceFormatKHR ChooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats) {
408 for (const auto& availableFormat : availableFormats)
409 {
410 if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
411 {
412 return availableFormat;
413 }
414 }
415
416 return availableFormats[0];
417 }
418 VkPresentModeKHR ChooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes)
419 {
420 for (const auto& availablePresentMode : availablePresentModes) {
421 if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) return availablePresentMode;
422 }
423
424 return VK_PRESENT_MODE_FIFO_KHR;
425 }
426 VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities)
427 {
428 if (capabilities.currentExtent.width != std::numeric_limits<u32>::max()) return capabilities.currentExtent;
429 else
430 {
431 int width, height;
432 glfwGetFramebufferSize(glfwWindow, &width, &height);
433
434 VkExtent2D actualExtent = {
435 static_cast<u32>(width),
436 static_cast<u32>(height)
437 };
438
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);
441
442 return actualExtent;
443 }
444 }
445 struct QueueFamilyIndices
446 {
447 std::optional<u32> graphicsFamily;
448 std::optional<u32> presentFamily;
449
450 bool isComplete()
451 {
452 return graphicsFamily.has_value() && presentFamily.has_value();
453 }
454 };
455 QueueFamilyIndices FindQueueFamilies(VkPhysicalDevice device)
456 {
457 QueueFamilyIndices indices {};
458
459 u32 queueFamilyCount = 0;
460 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
461 std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
462 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
463
464 u32 i = 0;
465 for (const auto& queueFamily : queueFamilies)
466 {
467 // Graphics family
468 if (queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) indices.graphicsFamily = i;
469
470 // present family
471 VkBool32 presentSupport = false;
472 vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
473 if (presentSupport) indices.presentFamily = i;
474
475 if (indices.isComplete()) break;;
476 }
477
478 return indices;
479 }
480 void CreateSwapChain()
481 {
482 SwapChainSupportDetails swapChainSupport = QuerySwapChainSupport(physicalDevice);
483
484 VkSurfaceFormatKHR surfaceFormat = ChooseSwapSurfaceFormat(swapChainSupport.formats);
485 VkPresentModeKHR presentMode = ChooseSwapPresentMode(swapChainSupport.presentModes);
486 VkExtent2D extent = ChooseSwapExtent(swapChainSupport.capabilities);
487
488 u32 imageCount = swapChainSupport.capabilities.minImageCount + 1;
489 if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount)
490 imageCount = swapChainSupport.capabilities.maxImageCount;
491
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;
501
502 QueueFamilyIndices indices = FindQueueFamilies(physicalDevice);
503 u32 queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()};
504
505 if (indices.graphicsFamily != indices.presentFamily)
506 {
507 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
508 createInfo.queueFamilyIndexCount = 2;
509 createInfo.pQueueFamilyIndices = queueFamilyIndices;
510 }
511 else
512 {
513 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
514 createInfo.queueFamilyIndexCount = 0;
515 createInfo.pQueueFamilyIndices = nullptr;
516 }
517 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
518 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
519
520 createInfo.presentMode = presentMode;
521 createInfo.clipped = VK_TRUE;
522 createInfo.oldSwapchain = VK_NULL_HANDLE;
523
524 if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) Error("Failed to create swap chain");
525
526 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
527 swapChainImages.resize(imageCount);
528 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
529
530 swapChainImageFormat = surfaceFormat.format;
531 swapChainExtent = extent;
532 }
533
534 // -------------------------------------
535 // Image Views
536 // -------------------------------------
537 void CreateImageViews()
538 {
539 swapChainImageViews.resize(swapChainImages.size());
540 for (size_t i = 0; i < swapChainImages.size(); i++)
541 {
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;
556
557 if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) Error("Failed to create image views");
558 }
559 }
560
561 // -------------------------------------
562 // Select GPU
563 // -------------------------------------
564 bool CheckDeviceExtensionSupport(VkPhysicalDevice device)
565 {
566 u32 extensionCount;
567 vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
568
569 std::vector<VkExtensionProperties> availableExtensions(extensionCount);
570 vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());
571
572 std::set<std::string> requiredExtentions(deviceExtentions.begin(), deviceExtentions.end());
573
574 for (const auto& extension : availableExtensions) requiredExtentions.erase(extension.extensionName);
575 return requiredExtentions.empty();
576 }
577 std::pair<u32, VkPhysicalDevice> RateDeviceSuitability(VkPhysicalDevice device)
578 {
579 VkPhysicalDeviceProperties properties;
580 vkGetPhysicalDeviceProperties(device, &properties);
581 VkPhysicalDeviceFeatures features;
582 vkGetPhysicalDeviceFeatures(device, &features);
583
584 u32 score = 1; // if score is equal to zero then device doesn't meet minimum requirements
585 const std::pair BELOW_MINIMUM_SPEC = std::make_pair((u32)0, device);
586
587 // Dedicated GPUs 90% of the time are faster
588 if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) score += DISCRETE_GPU_SCORE_BOOST;
589
590 // Maximum size of textures is a good measure of GPU performance
591 score += properties.limits.maxImageDimension2D;
592
593 // Debug output device scores
594 if (enableValidationLayers)
595 {
596 VkPhysicalDeviceProperties deviceProperties;
597 vkGetPhysicalDeviceProperties(device, &deviceProperties);
598 std::cout << "Device and its score: " << deviceProperties.deviceName << " : " << score << '\n';
599 }
600
601 // Check swap chain
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;
605
606 // Minimum specifications
607 if
608 (
609 !features.geometryShader ||
610
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;
615
616 if (!FindQueueFamilies(device).graphicsFamily.has_value()) return BELOW_MINIMUM_SPEC;
617
618 return std::make_pair(score, device);
619 }
620 void PickPhysicalDevice()
621 {
622 u32 deviceCount = 0;
623 vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
624
625 if (deviceCount == 0) Error("Couldn't find any GPU with Vulkan support :(");
626
627 std::vector<VkPhysicalDevice> devices(deviceCount);
628 vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
629
630 std::vector<std::pair<u32, VkPhysicalDevice>> candidates;
631
632 // Checks for GPUs that supports Vulkan
633 for (const auto& device : devices) candidates.push_back(RateDeviceSuitability(device));
634
635 auto selectedDevice = std::max_element(
636 candidates.begin(),
637 candidates.end(),
638 [](const auto& a, const auto& b)
639 {
640 return a.first < b.first;
641 }
642 );
643 if ((*selectedDevice).first == 0) Error("Couldn't find suitable GPU :(");
644
645 physicalDevice = (*selectedDevice).second;
646 }
647
648 // -------------------------------------
649 // Create Device
650 // -------------------------------------
651 void CreateLogicalDevice()
652 {
653 QueueFamilyIndices indices = FindQueueFamilies(physicalDevice);
654
655 std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
656 std::set<u32> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
657
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);
666 }
667
668 VkDeviceQueueCreateInfo queueCreateInfo{};
669 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
670 queueCreateInfo.queueFamilyIndex = indices.graphicsFamily.value();
671 queueCreateInfo.queueCount = 1;
672
673 queueCreateInfo.pQueuePriorities = &queuePriority;
674
675 VkPhysicalDeviceFeatures deviceFeatures{};
676
677 VkDeviceCreateInfo createInfo{};
678 createInfo.queueCreateInfoCount = static_cast<u32>(queueCreateInfos.size());
679 createInfo.pQueueCreateInfos = queueCreateInfos.data();
680
681 createInfo.pEnabledFeatures = &deviceFeatures;
682
683 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
684 createInfo.pQueueCreateInfos = &queueCreateInfo;
685 createInfo.queueCreateInfoCount = 1;
686
687 createInfo.enabledExtensionCount = static_cast<u32>(deviceExtentions.size());
688 createInfo.ppEnabledExtensionNames = deviceExtentions.data();
689
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);
693 }
694
695 // -------------------------------------
696 // Surface
697 // -------------------------------------
698 void CreateSurface()
699 {
700 // Using glfw should mean its not OS dependent
701 if (glfwCreateWindowSurface(instance, glfwWindow, NULL, &surface) != VK_SUCCESS)
702 {
703 Error("Failed to create window surface");
704 }
705 }
706
707 // -------------------------------------
708 // Graphics pipeline and render pass
709 // -------------------------------------
710 VkShaderModule CreateShaderModule(const std::vector<char>& code)
711 {
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());
716
717 VkShaderModule shaderModule;
718 if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) Error("failed to create shader module");
719 return shaderModule;
720 }
721 void CreateGraphicsPipeline()
722 {
723 auto vertShaderCode = ReadFile("shaders/vert.spv");
724 auto fragShaderCode = ReadFile("shaders/frag.spv");
725
726 VkShaderModule vertShaderModule = CreateShaderModule(vertShaderCode);
727 VkShaderModule fragShaderModule = CreateShaderModule(fragShaderCode);
728
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";
734
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";
740
741 VkPipelineShaderStageCreateInfo shaderStages[] = {vertShaderStageInfo, fragShaderStageInfo};
742
743 VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
744 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
745 vertexInputInfo.vertexBindingDescriptionCount = 0;
746 vertexInputInfo.vertexAttributeDescriptionCount = 0;
747
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;
752
753 VkPipelineViewportStateCreateInfo viewportState{};
754 viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
755 viewportState.viewportCount = 1;
756 viewportState.scissorCount = 1;
757
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;
767
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;
772
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;
776
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;
787
788 std::vector<VkDynamicState> dynamicStates = {
789 VK_DYNAMIC_STATE_VIEWPORT,
790 VK_DYNAMIC_STATE_SCISSOR
791 };
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();
796
797 VkPipelineLayoutCreateInfo pipelineLayoutInfo {};
798 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
799 pipelineLayoutInfo.setLayoutCount = 0;
800 pipelineLayoutInfo.pushConstantRangeCount = 0;
801
802 if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) Error("failed to create pipeline layout");
803
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;
819
820 if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) Error("failed to create graphics pipeline");
821
822 vkDestroyShaderModule(device, fragShaderModule, nullptr);
823 vkDestroyShaderModule(device, vertShaderModule, nullptr);
824 }
825 void CreateRenderPass()
826 {
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;
836
837 VkAttachmentReference colorAttachmentRef{};
838 colorAttachmentRef.attachment = 0;
839 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
840
841 VkSubpassDescription subpass{};
842 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
843 subpass.colorAttachmentCount = 1;
844 subpass.pColorAttachments = &colorAttachmentRef;
845
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;
853
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;
862
863 if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) Error("failed to create render pass");
864 }
865
866 // -------------------------------------
867 // Frame and command buffers
868 // -------------------------------------
869 void CreateFramebuffers()
870 {
871 swapChainFramebuffers.resize(swapChainImageViews.size());
872
873 for (size_t i = 0; i < swapChainImageViews.size(); i++) {
874 VkImageView attachments[] = {
875 swapChainImageViews[i]
876 };
877
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;
886
887 if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) Error("failed to create framebuffer");
888 }
889 }
890 void CreateCommandPool()
891 {
892 QueueFamilyIndices queueFamilyIndices = FindQueueFamilies(physicalDevice);
893
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();
898
899 if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) Error("Failed to create command pool");
900 }
901 void CreateCommandBuffers()
902 {
903 commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
904
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();
910
911 if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) Error("failed to allocate command buffers");
912 }
913 VkClearColorValue ConvertBackgroundColour()
914 {
915 return {{
916 (static_cast<float>(window::backgroundColour.r) / 255),
917 (static_cast<float>(window::backgroundColour.g) / 255),
918 (static_cast<float>(window::backgroundColour.b) / 255),
919 (static_cast<float>(window::backgroundColour.a) / 255)
920 }};
921 }
922 void RecordCommandBuffer(VkCommandBuffer commandBuffer, u32 imageIndex)
923 {
924 VkCommandBufferBeginInfo beginInfo{};
925 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
926 beginInfo.flags = 0; // Optional
927 beginInfo.pInheritanceInfo = nullptr; // Optional
928
929 if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) Error("failed to begin recording command buffer");
930
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);
941
942 vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
943 VkViewport viewport{};
944 viewport.x = 0.0f;
945 viewport.y = 0.0f;
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);
951
952 VkRect2D scissor{};
953 scissor.offset = {0, 0};
954 scissor.extent = swapChainExtent;
955 vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
956
957 // draw triangle
958 vkCmdDraw(commandBuffer, 3, 1, 0, 0);
959
960 vkCmdEndRenderPass(commandBuffer);
961 if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) Error("failed to record command buffer");
962 }
963
964 // -------------------------------------
965 // Draw frame
966 // -------------------------------------
967 void CreateSyncObjects()
968 {
969 frames.resize(MAX_FRAMES_IN_FLIGHT);
970
971 VkSemaphoreCreateInfo semaphoreInfo {};
972 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
973
974 VkFenceCreateInfo fenceInfo {};
975 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
976 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
977
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");
982 }
983 void DrawFrame()
984 {
985 vkWaitForFences(device, 1, &frames[currentFrame].inFlightFence, VK_TRUE, UINT64_MAX);
986 vkResetFences(device, 1, &frames[currentFrame].inFlightFence);
987
988 uint32_t imageIndex;
989 vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, frames[currentFrame].imageAvailableSemaphore, VK_NULL_HANDLE, &imageIndex);
990
991 vkResetCommandBuffer(commandBuffers[currentFrame], 0);
992 RecordCommandBuffer(commandBuffers[currentFrame], imageIndex);
993
994 VkSubmitInfo submitInfo {};
995 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
996
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];
1004
1005 VkSemaphore signalSemaphores[] = { frames[currentFrame].renderFinishedSemaphore };
1006 submitInfo.signalSemaphoreCount = 1;
1007 submitInfo.pSignalSemaphores = signalSemaphores;
1008
1009 if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, frames[currentFrame].inFlightFence) != VK_SUCCESS) Error("failed to submit draw command buffer");
1010
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;
1018
1019 VkPresentInfoKHR presentInfo{};
1020 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
1021
1022 presentInfo.waitSemaphoreCount = 1;
1023 presentInfo.pWaitSemaphores = signalSemaphores;
1024
1025 VkSwapchainKHR swapChains[] = { swapChain };
1026 presentInfo.swapchainCount = 1;
1027 presentInfo.pSwapchains = swapChains;
1028 presentInfo.pImageIndices = &imageIndex;
1029 presentInfo.pResults = nullptr;
1030
1031 vkQueuePresentKHR(presentQueue, &presentInfo);
1032 currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
1033 }
1034
1035 // -------------------------------------
1036 // Debug and Miscellaneous
1037 // -------------------------------------
1038 void DebugDeviceDetails()
1039 {
1040 VkPhysicalDeviceProperties deviceProperties;
1041 vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
1042
1043 static const std::unordered_map<u32, const char*> gpuVendors = {
1044 { 4098, "AMD" },
1045 { 4318, "NVIDIA" },
1046 { 32902, "INTEL" },
1047 { 5045, "ARM "}
1048 };
1049
1050 static const u8 LABEL_WIDTH = 15,
1051 VALUE_WIDTH = 35;
1052 auto PrintDetail = [](const char* label, const auto& value)
1053 {
1054 std::cout << std::setw(LABEL_WIDTH) << std::left << label << ": "
1055 << std::setw(VALUE_WIDTH) << std::left << value << "\n";
1056 };
1057
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)) + ")");
1063
1064 PrintDetail("Driver version", deviceProperties.driverVersion);
1065 PrintDetail("Device ID", deviceProperties.deviceID);
1066
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 + ")");
1070 }
1071 void ClampSetResolution(u32 *resolution, const u32 maxResolution)
1072 {
1073 // Floors the value
1074 *resolution = *resolution < maxResolution ? *resolution : maxResolution;
1075 }
1076 void Error(const char* msg)
1077 {
1078 if (enableValidationLayers) std::cerr << "Error: " << msg << '\n';
1079 window::Close();
1080 throw std::runtime_error(msg);
1081 }
1082 static std::vector<char> ReadFile(const std::string& filename)
1083 {
1084 // TODO: probably don't use this
1085 std::ifstream file(filename, std::ios::ate | std::ios::binary);
1086
1087 if (!file.is_open()) Error("failed to open file!");
1088
1089 size_t fileSize = (size_t) file.tellg();
1090 std::vector<char> buffer(fileSize);
1091
1092 file.seekg(0);
1093 file.read(buffer.data(), fileSize);
1094
1095 file.close();
1096
1097 return buffer;
1098 }
1099}
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....
Definition window.cpp:135
const char * title
Title of the window.
Definition window.cpp:136
void SetWindowResolution(u32 width, u32 height)
Sets the window resolution.
Definition window.cpp:217
u32Vec2 GetWindowResolution()
Gets the window resolution.
Definition window.cpp:239
Colour backgroundColour
Background colour, has alias backgroundColor.
Definition window.cpp:138
void SetRenderResolution(u32 width, u32 height)
Sets the render resolution.
Definition window.cpp:230
void Close()
Closes the window and cleans up Vulkan.
Definition window.cpp:182
bool isResizable
Going to be flags later on...
Definition window.cpp:137
void Init()
alias for backgroundColour
Definition window.cpp:140
u32Vec2 GetRenderResolution()
Gets the render resolution.
Definition window.cpp:238
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.