source: opengl-game/vulkan-game.cpp@ ad31ec7

feature/imgui-sdl points-test
Last change on this file since ad31ec7 was ad31ec7, checked in by Dmitry Portnoy <dmp1488@…>, 5 years ago

Fix the SDL overlay in Windows 10

  • Property mode set to 100644
File size: 80.5 KB
Line 
1/*
2DESIGN GUIDE
3
4-I should store multiple buffers (e.g. vertex and index buffers) in the same VkBuffer and use offsets into it
5-For specifying a separate transform for each model, I can specify a descriptorCount > ` in the ubo layout binding
6-Name class instance variables that are pointers (and possibly other pointer variables as well) like pVarName
7*/
8
9#include "game-gui-glfw.hpp"
10
11#include "game-gui-sdl.hpp"
12
13//#define _USE_MATH_DEFINES // Will be needed when/if I need to # include <cmath>
14
15#define GLM_FORCE_RADIANS
16#define GLM_FORCE_DEPTH_ZERO_TO_ONE
17#include <glm/glm.hpp>
18#include <glm/gtc/matrix_transform.hpp>
19
20#define STB_IMAGE_IMPLEMENTATION
21#include "stb_image.h" // TODO: Probably switch to SDL_image
22
23#include <iostream>
24#include <fstream>
25#include <algorithm>
26#include <vector>
27#include <array>
28#include <set>
29#include <optional>
30#include <chrono>
31
32using namespace std;
33
34// TODO: Maybe add asserts for testing
35
36const int SCREEN_WIDTH = 800;
37const int SCREEN_HEIGHT = 600;
38
39const int MAX_FRAMES_IN_FLIGHT = 2;
40
41#ifdef NDEBUG
42 const bool enableValidationLayers = false;
43#else
44 const bool enableValidationLayers = true;
45#endif
46
47const vector<const char*> validationLayers = {
48 "VK_LAYER_KHRONOS_validation"
49};
50
51const vector<const char*> deviceExtensions = {
52 VK_KHR_SWAPCHAIN_EXTENSION_NAME
53};
54
55struct QueueFamilyIndices {
56 optional<uint32_t> graphicsFamily;
57 optional<uint32_t> presentFamily;
58
59 bool isComplete() {
60 return graphicsFamily.has_value() && presentFamily.has_value();
61 }
62};
63
64struct SwapChainSupportDetails {
65 VkSurfaceCapabilitiesKHR capabilities;
66 vector<VkSurfaceFormatKHR> formats;
67 vector<VkPresentModeKHR> presentModes;
68};
69
70struct Vertex {
71 glm::vec3 pos;
72 glm::vec3 color;
73 glm::vec2 texCoord;
74
75 static VkVertexInputBindingDescription getBindingDescription() {
76 VkVertexInputBindingDescription bindingDescription = {};
77
78 bindingDescription.binding = 0;
79 bindingDescription.stride = sizeof(Vertex);
80 bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
81
82 return bindingDescription;
83 }
84
85 static array<VkVertexInputAttributeDescription, 3> getAttributeDescriptions() {
86 array<VkVertexInputAttributeDescription, 3> attributeDescriptions = {};
87
88 attributeDescriptions[0].binding = 0;
89 attributeDescriptions[0].location = 0;
90 attributeDescriptions[0].format = VK_FORMAT_R32G32B32_SFLOAT;
91 attributeDescriptions[0].offset = offsetof(Vertex, pos);
92
93 attributeDescriptions[1].binding = 0;
94 attributeDescriptions[1].location = 1;
95 attributeDescriptions[1].format = VK_FORMAT_R32G32B32_SFLOAT;
96 attributeDescriptions[1].offset = offsetof(Vertex, color);
97
98 attributeDescriptions[2].binding = 0;
99 attributeDescriptions[2].location = 2;
100 attributeDescriptions[2].format = VK_FORMAT_R32G32_SFLOAT;
101 attributeDescriptions[2].offset = offsetof(Vertex, texCoord);
102
103 return attributeDescriptions;
104 }
105};
106
107struct UniformBufferObject {
108 alignas(16) glm::mat4 model;
109 alignas(16) glm::mat4 view;
110 alignas(16) glm::mat4 proj;
111};
112
113struct GraphicsPipelineInfo {
114 VkPipelineLayout pipelineLayout;
115 VkPipeline pipeline;
116
117 VkDescriptorPool descriptorPool;
118 VkDescriptorSetLayout descriptorSetLayout;
119 vector<VkDescriptorSet> descriptorSets;
120
121 VkBuffer vertexBuffer;
122 VkDeviceMemory vertexBufferMemory;
123 size_t numVertices;
124
125 VkBuffer indexBuffer;
126 VkDeviceMemory indexBufferMemory;
127 size_t numIndices;
128};
129
130VkResult CreateDebugUtilsMessengerEXT(VkInstance instance,
131 const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
132 const VkAllocationCallbacks* pAllocator,
133 VkDebugUtilsMessengerEXT* pDebugMessenger) {
134 auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
135
136 if (func != nullptr) {
137 return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
138 } else {
139 return VK_ERROR_EXTENSION_NOT_PRESENT;
140 }
141}
142
143void DestroyDebugUtilsMessengerEXT(VkInstance instance,
144 VkDebugUtilsMessengerEXT debugMessenger,
145 const VkAllocationCallbacks* pAllocator) {
146 auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
147
148 if (func != nullptr) {
149 func(instance, debugMessenger, pAllocator);
150 }
151}
152
153class VulkanGame {
154 public:
155 void run() {
156 if (initWindow() == RTWO_ERROR) {
157 return;
158 }
159 initVulkan();
160 mainLoop();
161 cleanup();
162 }
163
164 private:
165 GameGui* gui = new GameGui_SDL();
166 SDL_Window* window = nullptr;
167
168 // TODO: Come up with more descriptive names for these
169 SDL_Renderer* gRenderer = nullptr;
170 SDL_Texture* uiOverlay = nullptr;
171
172 TTF_Font* gFont = nullptr;
173 SDL_Texture* uiText = nullptr;
174 SDL_Texture* uiImage = nullptr;
175
176 VkInstance instance;
177 VkDebugUtilsMessengerEXT debugMessenger;
178 VkSurfaceKHR surface;
179
180 VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
181 VkDevice device;
182
183 VkQueue graphicsQueue;
184 VkQueue presentQueue;
185
186 VkSwapchainKHR swapChain;
187 vector<VkImage> swapChainImages;
188 VkFormat swapChainImageFormat;
189 VkExtent2D swapChainExtent;
190 vector<VkImageView> swapChainImageViews;
191 vector<VkFramebuffer> swapChainFramebuffers;
192
193 VkRenderPass renderPass;
194
195 VkCommandPool commandPool;
196 vector<VkCommandBuffer> commandBuffers;
197
198 // The images and the sampler are used to store data for specific attributes. I probably
199 // want to keep them separate from the GraphicsPipelineInfo objects and start passing
200 // references to them once I start defining uniform and varying attributes in GraphicsPipelineInfo objects
201
202 VkImage depthImage;
203 VkDeviceMemory depthImageMemory;
204 VkImageView depthImageView;
205
206 VkImage textureImage;
207 VkDeviceMemory textureImageMemory;
208 VkImageView textureImageView;
209
210 VkImage overlayImage;
211 VkDeviceMemory overlayImageMemory;
212 VkImageView overlayImageView;
213
214 VkImage sdlOverlayImage;
215 VkDeviceMemory sdlOverlayImageMemory;
216 VkImageView sdlOverlayImageView;
217
218 VkSampler textureSampler;
219
220 // These are currently to store the MVP matrix
221 // I should figure out if it makes sense to use them for other uniforms in the future
222 // If not, I should rename them to better indicate their puprose.
223 // I should also decide if I can use these for all shaders, or if I need a separapte set of buffers for each one
224 vector<VkBuffer> uniformBuffers;
225 vector<VkDeviceMemory> uniformBuffersMemory;
226
227 GraphicsPipelineInfo scenePipeline;
228 GraphicsPipelineInfo overlayPipeline;
229
230 vector<VkSemaphore> imageAvailableSemaphores;
231 vector<VkSemaphore> renderFinishedSemaphores;
232 vector<VkFence> inFlightFences;
233
234 size_t currentFrame = 0;
235
236 bool framebufferResized = false;
237
238 // TODO: Make make some more initi functions, or call this initUI if the
239 // amount of things initialized here keeps growing
240 bool initWindow() {
241 // TODO: Put all fonts, textures, and images in the assets folder
242
243 if (gui->Init() == RTWO_ERROR) {
244 cout << "UI library could not be initialized!" << endl;
245 cout << SDL_GetError() << endl;
246 return RTWO_ERROR;
247 }
248 cout << "GUI init succeeded" << endl;
249
250 window = (SDL_Window*) gui->CreateWindow("Vulkan Game", SCREEN_WIDTH, SCREEN_HEIGHT);
251 if (window == nullptr) {
252 cout << "Window could not be created!" << endl;
253 return RTWO_ERROR;
254 }
255
256 gRenderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
257 if (gRenderer == nullptr) {
258 cout << "Renderer could not be created! SDL Error: " << SDL_GetError() << endl;
259 return RTWO_ERROR;
260 }
261
262 uiOverlay = SDL_CreateTexture(gRenderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, SCREEN_WIDTH, SCREEN_HEIGHT);
263 if (uiOverlay == nullptr) {
264 cout << "Unable to create blank texture! SDL Error: " << SDL_GetError() << endl;
265 }
266 if (SDL_SetTextureBlendMode(uiOverlay, SDL_BLENDMODE_BLEND) != 0) {
267 cout << "Unable to set texture blend mode! SDL Error: " << SDL_GetError() << endl;
268 }
269
270 gFont = TTF_OpenFont("fonts/lazy.ttf", 28);
271 if (gFont == nullptr) {
272 cout << "Failed to load lazy font! SDL_ttf Error: " << TTF_GetError() << endl;
273 return RTWO_ERROR;
274 }
275
276 SDL_Color textColor = { 0, 0, 0 };
277
278 SDL_Surface* textSurface = TTF_RenderText_Solid(gFont, "Great sucess!", textColor);
279 if (textSurface == nullptr) {
280 cout << "Unable to render text surface! SDL_ttf Error: " << TTF_GetError() << endl;
281 return RTWO_ERROR;
282 }
283
284 uiText = SDL_CreateTextureFromSurface(gRenderer, textSurface);
285 if (uiText == nullptr) {
286 cout << "Unable to create texture from rendered text! SDL Error: " << SDL_GetError() << endl;
287 SDL_FreeSurface(textSurface);
288 return RTWO_ERROR;
289 }
290
291 SDL_FreeSurface(textSurface);
292
293 // TODO: Load a PNG instead
294 SDL_Surface* uiImageSurface = SDL_LoadBMP("assets/images/spaceship.bmp");
295 if (uiImageSurface == nullptr) {
296 cout << "Unable to load image " << "spaceship.bmp" << "! SDL Error: " << SDL_GetError() << endl;
297 return RTWO_ERROR;
298 }
299
300 uiImage = SDL_CreateTextureFromSurface(gRenderer, uiImageSurface);
301 if (uiImage == nullptr) {
302 cout << "Unable to create texture from BMP surface! SDL Error: " << SDL_GetError() << endl;
303 SDL_FreeSurface(uiImageSurface);
304 return RTWO_ERROR;
305 }
306
307 SDL_FreeSurface(uiImageSurface);
308
309 return RTWO_SUCCESS;
310 }
311
312 void initVulkan() {
313 createInstance();
314 setupDebugMessenger();
315 createSurface();
316 pickPhysicalDevice();
317 createLogicalDevice();
318 createSwapChain();
319 createImageViews();
320 createRenderPass();
321
322 createDescriptorSetLayout(scenePipeline);
323 createGraphicsPipeline("shaders/vert.spv", "shaders/frag.spv", scenePipeline);
324
325 createDescriptorSetLayout(overlayPipeline);
326 createGraphicsPipeline("shaders/vert.spv", "shaders/frag.spv", overlayPipeline);
327
328 createCommandPool();
329 createDepthResources();
330 createFramebuffers();
331
332 createImageResources("textures/texture.jpg", textureImage, textureImageMemory, textureImageView);
333 createImageResourcesFromSDLTexture(uiOverlay, sdlOverlayImage, sdlOverlayImageMemory, sdlOverlayImageView);
334 createTextureSampler();
335
336 createShaderBuffers(scenePipeline, {
337 {{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
338 {{ 0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
339 {{ 0.5f, 0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
340 {{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
341
342 {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
343 {{ 0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
344 {{ 0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
345 {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
346 }, {
347 0, 1, 2, 2, 3, 0,
348 4, 5, 6, 6, 7, 4
349 });
350
351 createShaderBuffers(overlayPipeline, {
352 // Filler vertices to increase the overlay vertex indices to at least 8
353 {{-1.0f, -1.0f, 3.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
354 {{-1.0f, -1.0f, 3.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
355 {{-1.0f, -1.0f, 3.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
356 {{-1.0f, -1.0f, 3.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
357 {{-1.0f, -1.0f, 3.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
358 {{-1.0f, -1.0f, 3.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
359 {{-1.0f, -1.0f, 3.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
360 {{-1.0f, -1.0f, 3.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
361
362 {{-1.0f, 1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
363 {{ 1.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
364 {{ 1.0f, -1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
365 {{-1.0f, -1.0f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
366 }, {
367 8, 9, 10, 10, 11, 8
368 });
369
370 createUniformBuffers();
371
372 createDescriptorPool(scenePipeline);
373 createDescriptorSets(scenePipeline);
374
375 createDescriptorPool(overlayPipeline);
376 createDescriptorSets(overlayPipeline);
377
378 createCommandBuffers();
379 createSyncObjects();
380 }
381
382 void createInstance() {
383 if (enableValidationLayers && !checkValidationLayerSupport()) {
384 throw runtime_error("validation layers requested, but not available!");
385 }
386
387 VkApplicationInfo appInfo = {};
388 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
389 appInfo.pApplicationName = "Vulkan Game";
390 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
391 appInfo.pEngineName = "No Engine";
392 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
393 appInfo.apiVersion = VK_API_VERSION_1_0;
394
395 VkInstanceCreateInfo createInfo = {};
396 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
397 createInfo.pApplicationInfo = &appInfo;
398
399 vector<const char*> extensions = getRequiredExtensions();
400 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
401 createInfo.ppEnabledExtensionNames = extensions.data();
402
403 cout << endl << "Extensions:" << endl;
404 for (const char* extensionName : extensions) {
405 cout << extensionName << endl;
406 }
407 cout << endl;
408
409 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
410 if (enableValidationLayers) {
411 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
412 createInfo.ppEnabledLayerNames = validationLayers.data();
413
414 populateDebugMessengerCreateInfo(debugCreateInfo);
415 createInfo.pNext = &debugCreateInfo;
416 } else {
417 createInfo.enabledLayerCount = 0;
418
419 createInfo.pNext = nullptr;
420 }
421
422 if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
423 throw runtime_error("failed to create instance!");
424 }
425 }
426
427 bool checkValidationLayerSupport() {
428 uint32_t layerCount;
429 vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
430
431 vector<VkLayerProperties> availableLayers(layerCount);
432 vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
433
434 for (const char* layerName : validationLayers) {
435 bool layerFound = false;
436
437 for (const auto& layerProperties : availableLayers) {
438 if (strcmp(layerName, layerProperties.layerName) == 0) {
439 layerFound = true;
440 break;
441 }
442 }
443
444 if (!layerFound) {
445 return false;
446 }
447 }
448
449 return true;
450 }
451
452 vector<const char*> getRequiredExtensions() {
453 vector<const char*> extensions = gui->GetRequiredExtensions();
454
455 if (enableValidationLayers) {
456 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
457 }
458
459 return extensions;
460 }
461
462 void setupDebugMessenger() {
463 if (!enableValidationLayers) return;
464
465 VkDebugUtilsMessengerCreateInfoEXT createInfo;
466 populateDebugMessengerCreateInfo(createInfo);
467
468 if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
469 throw runtime_error("failed to set up debug messenger!");
470 }
471 }
472
473 void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
474 createInfo = {};
475 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
476 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;
477 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;
478 createInfo.pfnUserCallback = debugCallback;
479 }
480
481 void createSurface() {
482 if (gui->CreateVulkanSurface(instance, &surface) == RTWO_ERROR) {
483 throw runtime_error("failed to create window surface!");
484 }
485 }
486
487 void pickPhysicalDevice() {
488 uint32_t deviceCount = 0;
489 vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
490
491 if (deviceCount == 0) {
492 throw runtime_error("failed to find GPUs with Vulkan support!");
493 }
494
495 vector<VkPhysicalDevice> devices(deviceCount);
496 vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
497
498 cout << endl << "Graphics cards:" << endl;
499 for (const VkPhysicalDevice& device : devices) {
500 if (isDeviceSuitable(device)) {
501 physicalDevice = device;
502 break;
503 }
504 }
505 cout << endl;
506
507 if (physicalDevice == VK_NULL_HANDLE) {
508 throw runtime_error("failed to find a suitable GPU!");
509 }
510 }
511
512 bool isDeviceSuitable(VkPhysicalDevice device) {
513 VkPhysicalDeviceProperties deviceProperties;
514 vkGetPhysicalDeviceProperties(device, &deviceProperties);
515
516 cout << "Device: " << deviceProperties.deviceName << endl;
517
518 QueueFamilyIndices indices = findQueueFamilies(device);
519 bool extensionsSupported = checkDeviceExtensionSupport(device);
520 bool swapChainAdequate = false;
521
522 if (extensionsSupported) {
523 SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device);
524 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
525 }
526
527 VkPhysicalDeviceFeatures supportedFeatures;
528 vkGetPhysicalDeviceFeatures(device, &supportedFeatures);
529
530 return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
531 }
532
533 bool checkDeviceExtensionSupport(VkPhysicalDevice device) {
534 uint32_t extensionCount;
535 vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
536
537 vector<VkExtensionProperties> availableExtensions(extensionCount);
538 vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());
539
540 set<string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
541
542 for (const auto& extension : availableExtensions) {
543 requiredExtensions.erase(extension.extensionName);
544 }
545
546 return requiredExtensions.empty();
547 }
548
549 void createLogicalDevice() {
550 QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
551
552 vector<VkDeviceQueueCreateInfo> queueCreateInfos;
553 set<uint32_t> uniqueQueueFamilies = {indices.graphicsFamily.value(), indices.presentFamily.value()};
554
555 float queuePriority = 1.0f;
556 for (uint32_t queueFamily : uniqueQueueFamilies) {
557 VkDeviceQueueCreateInfo queueCreateInfo = {};
558 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
559 queueCreateInfo.queueFamilyIndex = queueFamily;
560 queueCreateInfo.queueCount = 1;
561 queueCreateInfo.pQueuePriorities = &queuePriority;
562
563 queueCreateInfos.push_back(queueCreateInfo);
564 }
565
566 VkPhysicalDeviceFeatures deviceFeatures = {};
567 deviceFeatures.samplerAnisotropy = VK_TRUE;
568
569 VkDeviceCreateInfo createInfo = {};
570 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
571 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
572 createInfo.pQueueCreateInfos = queueCreateInfos.data();
573
574 createInfo.pEnabledFeatures = &deviceFeatures;
575
576 createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
577 createInfo.ppEnabledExtensionNames = deviceExtensions.data();
578
579 // These fields are ignored by up-to-date Vulkan implementations,
580 // but it's a good idea to set them for backwards compatibility
581 if (enableValidationLayers) {
582 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
583 createInfo.ppEnabledLayerNames = validationLayers.data();
584 } else {
585 createInfo.enabledLayerCount = 0;
586 }
587
588 if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
589 throw runtime_error("failed to create logical device!");
590 }
591
592 vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
593 vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
594 }
595
596 void createSwapChain() {
597 SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice);
598
599 VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
600 VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);
601 VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities);
602
603 uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
604 if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) {
605 imageCount = swapChainSupport.capabilities.maxImageCount;
606 }
607
608 VkSwapchainCreateInfoKHR createInfo = {};
609 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
610 createInfo.surface = surface;
611 createInfo.minImageCount = imageCount;
612 createInfo.imageFormat = surfaceFormat.format;
613 createInfo.imageColorSpace = surfaceFormat.colorSpace;
614 createInfo.imageExtent = extent;
615 createInfo.imageArrayLayers = 1;
616 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
617
618 QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
619 uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()};
620
621 if (indices.graphicsFamily != indices.presentFamily) {
622 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
623 createInfo.queueFamilyIndexCount = 2;
624 createInfo.pQueueFamilyIndices = queueFamilyIndices;
625 } else {
626 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
627 createInfo.queueFamilyIndexCount = 0;
628 createInfo.pQueueFamilyIndices = nullptr;
629 }
630
631 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
632 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
633 createInfo.presentMode = presentMode;
634 createInfo.clipped = VK_TRUE;
635 createInfo.oldSwapchain = VK_NULL_HANDLE;
636
637 if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
638 throw runtime_error("failed to create swap chain!");
639 }
640
641 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
642 swapChainImages.resize(imageCount);
643 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
644
645 swapChainImageFormat = surfaceFormat.format;
646 swapChainExtent = extent;
647 }
648
649 SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) {
650 SwapChainSupportDetails details;
651
652 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);
653
654 uint32_t formatCount;
655 vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
656
657 if (formatCount != 0) {
658 details.formats.resize(formatCount);
659 vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
660 }
661
662 uint32_t presentModeCount;
663 vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
664
665 if (presentModeCount != 0) {
666 details.presentModes.resize(presentModeCount);
667 vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());
668 }
669
670 return details;
671 }
672
673 VkSurfaceFormatKHR chooseSwapSurfaceFormat(const vector<VkSurfaceFormatKHR>& availableFormats) {
674 for (const auto& availableFormat : availableFormats) {
675 if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
676 return availableFormat;
677 }
678 }
679
680 return availableFormats[0];
681 }
682
683 VkPresentModeKHR chooseSwapPresentMode(const vector<VkPresentModeKHR>& availablePresentModes) {
684 VkPresentModeKHR bestMode = VK_PRESENT_MODE_FIFO_KHR;
685
686 for (const auto& availablePresentMode : availablePresentModes) {
687 if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
688 return availablePresentMode;
689 }
690 else if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) {
691 bestMode = availablePresentMode;
692 }
693 }
694
695 return bestMode;
696 }
697
698 VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) {
699 if (capabilities.currentExtent.width != numeric_limits<uint32_t>::max()) {
700 return capabilities.currentExtent;
701 }
702 else {
703 int width, height;
704 gui->GetWindowSize(&width, &height);
705
706 VkExtent2D actualExtent = {
707 static_cast<uint32_t>(width),
708 static_cast<uint32_t>(height)
709 };
710
711 actualExtent.width = max(capabilities.minImageExtent.width, min(capabilities.maxImageExtent.width, actualExtent.width));
712 actualExtent.height = max(capabilities.minImageExtent.height, min(capabilities.maxImageExtent.height, actualExtent.height));
713
714 return actualExtent;
715 }
716 }
717
718 void createImageViews() {
719 swapChainImageViews.resize(swapChainImages.size());
720
721 for (size_t i = 0; i < swapChainImages.size(); i++) {
722 swapChainImageViews[i] = createImageView(swapChainImages[i], swapChainImageFormat, VK_IMAGE_ASPECT_COLOR_BIT);
723 }
724 }
725
726 void createRenderPass() {
727 VkAttachmentDescription colorAttachment = {};
728 colorAttachment.format = swapChainImageFormat;
729 colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
730 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
731 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
732 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
733 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
734 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
735 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
736
737 VkAttachmentReference colorAttachmentRef = {};
738 colorAttachmentRef.attachment = 0;
739 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
740
741 VkAttachmentDescription depthAttachment = {};
742 depthAttachment.format = findDepthFormat();
743 depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
744 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
745 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
746 depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
747 depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
748 depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
749 depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
750
751 VkAttachmentReference depthAttachmentRef = {};
752 depthAttachmentRef.attachment = 1;
753 depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
754
755 VkSubpassDescription subpass = {};
756 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
757 subpass.colorAttachmentCount = 1;
758 subpass.pColorAttachments = &colorAttachmentRef;
759 subpass.pDepthStencilAttachment = &depthAttachmentRef;
760
761 VkSubpassDependency dependency = {};
762 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
763 dependency.dstSubpass = 0;
764 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
765 dependency.srcAccessMask = 0;
766 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
767 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
768
769 array<VkAttachmentDescription, 2> attachments = { colorAttachment, depthAttachment };
770 VkRenderPassCreateInfo renderPassInfo = {};
771 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
772 renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
773 renderPassInfo.pAttachments = attachments.data();
774 renderPassInfo.subpassCount = 1;
775 renderPassInfo.pSubpasses = &subpass;
776 renderPassInfo.dependencyCount = 1;
777 renderPassInfo.pDependencies = &dependency;
778
779 if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
780 throw runtime_error("failed to create render pass!");
781 }
782 }
783
784 void createDescriptorSetLayout(GraphicsPipelineInfo& info) {
785 VkDescriptorSetLayoutBinding uboLayoutBinding = {};
786 uboLayoutBinding.binding = 0;
787 uboLayoutBinding.descriptorCount = 1;
788 uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
789 uboLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
790 uboLayoutBinding.pImmutableSamplers = nullptr;
791
792 VkDescriptorSetLayoutBinding samplerLayoutBinding = {};
793 samplerLayoutBinding.binding = 1;
794 samplerLayoutBinding.descriptorCount = 1;
795 samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
796 samplerLayoutBinding.pImmutableSamplers = nullptr;
797 samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
798
799 VkDescriptorSetLayoutBinding overlaySamplerLayoutBinding = {};
800 overlaySamplerLayoutBinding.binding = 2;
801 overlaySamplerLayoutBinding.descriptorCount = 1;
802 overlaySamplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
803 overlaySamplerLayoutBinding.pImmutableSamplers = nullptr;
804 overlaySamplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
805
806 array<VkDescriptorSetLayoutBinding, 3> bindings = { uboLayoutBinding, samplerLayoutBinding, overlaySamplerLayoutBinding };
807 VkDescriptorSetLayoutCreateInfo layoutInfo = {};
808 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
809 layoutInfo.bindingCount = static_cast<uint32_t>(bindings.size());
810 layoutInfo.pBindings = bindings.data();
811
812 if (vkCreateDescriptorSetLayout(device, &layoutInfo, nullptr, &info.descriptorSetLayout) != VK_SUCCESS) {
813 throw runtime_error("failed to create descriptor set layout!");
814 }
815 }
816
817 void createGraphicsPipeline(string vertShaderFile, string fragShaderFile, GraphicsPipelineInfo& info) {
818 auto vertShaderCode = readFile(vertShaderFile);
819 auto fragShaderCode = readFile(fragShaderFile);
820
821 VkShaderModule vertShaderModule = createShaderModule(vertShaderCode);
822 VkShaderModule fragShaderModule = createShaderModule(fragShaderCode);
823
824 VkPipelineShaderStageCreateInfo vertShaderStageInfo = {};
825 vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
826 vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
827 vertShaderStageInfo.module = vertShaderModule;
828 vertShaderStageInfo.pName = "main";
829
830 VkPipelineShaderStageCreateInfo fragShaderStageInfo = {};
831 fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
832 fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
833 fragShaderStageInfo.module = fragShaderModule;
834 fragShaderStageInfo.pName = "main";
835
836 VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
837
838 VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
839 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
840
841 auto bindingDescription = Vertex::getBindingDescription();
842 auto attributeDescriptions = Vertex::getAttributeDescriptions();
843
844 vertexInputInfo.vertexBindingDescriptionCount = 1;
845 vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
846 vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
847 vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
848
849 VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
850 inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
851 inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
852 inputAssembly.primitiveRestartEnable = VK_FALSE;
853
854 VkViewport viewport = {};
855 viewport.x = 0.0f;
856 viewport.y = 0.0f;
857 viewport.width = (float) swapChainExtent.width;
858 viewport.height = (float) swapChainExtent.height;
859 viewport.minDepth = 0.0f;
860 viewport.maxDepth = 1.0f;
861
862 VkRect2D scissor = {};
863 scissor.offset = { 0, 0 };
864 scissor.extent = swapChainExtent;
865
866 VkPipelineViewportStateCreateInfo viewportState = {};
867 viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
868 viewportState.viewportCount = 1;
869 viewportState.pViewports = &viewport;
870 viewportState.scissorCount = 1;
871 viewportState.pScissors = &scissor;
872
873 VkPipelineRasterizationStateCreateInfo rasterizer = {};
874 rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
875 rasterizer.depthClampEnable = VK_FALSE;
876 rasterizer.rasterizerDiscardEnable = VK_FALSE;
877 rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
878 rasterizer.lineWidth = 1.0f;
879 rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
880 rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
881 rasterizer.depthBiasEnable = VK_FALSE;
882
883 VkPipelineMultisampleStateCreateInfo multisampling = {};
884 multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
885 multisampling.sampleShadingEnable = VK_FALSE;
886 multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
887
888 VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
889 colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
890 colorBlendAttachment.blendEnable = VK_TRUE;
891 colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
892 colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
893 colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
894 colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
895 colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
896 colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
897
898 VkPipelineColorBlendStateCreateInfo colorBlending = {};
899 colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
900 colorBlending.logicOpEnable = VK_FALSE;
901 colorBlending.logicOp = VK_LOGIC_OP_COPY;
902 colorBlending.attachmentCount = 1;
903 colorBlending.pAttachments = &colorBlendAttachment;
904 colorBlending.blendConstants[0] = 0.0f;
905 colorBlending.blendConstants[1] = 0.0f;
906 colorBlending.blendConstants[2] = 0.0f;
907 colorBlending.blendConstants[3] = 0.0f;
908
909 VkPipelineDepthStencilStateCreateInfo depthStencil = {};
910 depthStencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
911 depthStencil.depthTestEnable = VK_TRUE;
912 depthStencil.depthWriteEnable = VK_TRUE;
913 depthStencil.depthCompareOp = VK_COMPARE_OP_LESS;
914 depthStencil.depthBoundsTestEnable = VK_FALSE;
915 depthStencil.minDepthBounds = 0.0f;
916 depthStencil.maxDepthBounds = 1.0f;
917 depthStencil.stencilTestEnable = VK_FALSE;
918 depthStencil.front = {};
919 depthStencil.back = {};
920
921 VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
922 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
923 pipelineLayoutInfo.setLayoutCount = 1;
924 pipelineLayoutInfo.pSetLayouts = &info.descriptorSetLayout;
925 pipelineLayoutInfo.pushConstantRangeCount = 0;
926
927 if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &info.pipelineLayout) != VK_SUCCESS) {
928 throw runtime_error("failed to create pipeline layout!");
929 }
930
931 VkGraphicsPipelineCreateInfo pipelineInfo = {};
932 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
933 pipelineInfo.stageCount = 2;
934 pipelineInfo.pStages = shaderStages;
935 pipelineInfo.pVertexInputState = &vertexInputInfo;
936 pipelineInfo.pInputAssemblyState = &inputAssembly;
937 pipelineInfo.pViewportState = &viewportState;
938 pipelineInfo.pRasterizationState = &rasterizer;
939 pipelineInfo.pMultisampleState = &multisampling;
940 pipelineInfo.pDepthStencilState = &depthStencil;
941 pipelineInfo.pColorBlendState = &colorBlending;
942 pipelineInfo.pDynamicState = nullptr;
943 pipelineInfo.layout = info.pipelineLayout;
944 pipelineInfo.renderPass = renderPass;
945 pipelineInfo.subpass = 0;
946 pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
947 pipelineInfo.basePipelineIndex = -1;
948
949 if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &info.pipeline) != VK_SUCCESS) {
950 throw runtime_error("failed to create graphics pipeline!");
951 }
952
953 vkDestroyShaderModule(device, vertShaderModule, nullptr);
954 vkDestroyShaderModule(device, fragShaderModule, nullptr);
955 }
956
957 VkShaderModule createShaderModule(const vector<char>& code) {
958 VkShaderModuleCreateInfo createInfo = {};
959 createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
960 createInfo.codeSize = code.size();
961 createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
962
963 VkShaderModule shaderModule;
964 if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
965 throw runtime_error("failed to create shader module!");
966 }
967
968 return shaderModule;
969 }
970
971 void createFramebuffers() {
972 swapChainFramebuffers.resize(swapChainImageViews.size());
973
974 for (size_t i = 0; i < swapChainImageViews.size(); i++) {
975 array <VkImageView, 2> attachments = {
976 swapChainImageViews[i],
977 depthImageView
978 };
979
980 VkFramebufferCreateInfo framebufferInfo = {};
981 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
982 framebufferInfo.renderPass = renderPass;
983 framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
984 framebufferInfo.pAttachments = attachments.data();
985 framebufferInfo.width = swapChainExtent.width;
986 framebufferInfo.height = swapChainExtent.height;
987 framebufferInfo.layers = 1;
988
989 if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
990 throw runtime_error("failed to create framebuffer!");
991 }
992 }
993 }
994
995 void createCommandPool() {
996 QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);
997
998 VkCommandPoolCreateInfo poolInfo = {};
999 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
1000 poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
1001 poolInfo.flags = 0;
1002
1003 if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
1004 throw runtime_error("failed to create graphics command pool!");
1005 }
1006 }
1007
1008 QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
1009 QueueFamilyIndices indices;
1010
1011 uint32_t queueFamilyCount = 0;
1012 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
1013
1014 vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
1015 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
1016
1017 int i = 0;
1018 for (const auto& queueFamily : queueFamilies) {
1019 if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
1020 indices.graphicsFamily = i;
1021 }
1022
1023 VkBool32 presentSupport = false;
1024 vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
1025
1026 if (queueFamily.queueCount > 0 && presentSupport) {
1027 indices.presentFamily = i;
1028 }
1029
1030 if (indices.isComplete()) {
1031 break;
1032 }
1033
1034 i++;
1035 }
1036
1037 return indices;
1038 }
1039
1040 void createDepthResources() {
1041 VkFormat depthFormat = findDepthFormat();
1042
1043 createImage(swapChainExtent.width, swapChainExtent.height, depthFormat, VK_IMAGE_TILING_OPTIMAL,
1044 VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, depthImage, depthImageMemory);
1045 depthImageView = createImageView(depthImage, depthFormat, VK_IMAGE_ASPECT_DEPTH_BIT);
1046
1047 transitionImageLayout(depthImage, depthFormat, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);
1048 }
1049
1050 VkFormat findDepthFormat() {
1051 return findSupportedFormat(
1052 { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
1053 VK_IMAGE_TILING_OPTIMAL,
1054 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
1055 );
1056 }
1057
1058 VkFormat findSupportedFormat(const vector<VkFormat>& candidates, VkImageTiling tiling,
1059 VkFormatFeatureFlags features) {
1060 for (VkFormat format : candidates) {
1061 VkFormatProperties props;
1062 vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &props);
1063
1064 if (tiling == VK_IMAGE_TILING_LINEAR &&
1065 (props.linearTilingFeatures & features) == features) {
1066 return format;
1067 } else if (tiling == VK_IMAGE_TILING_OPTIMAL &&
1068 (props.optimalTilingFeatures & features) == features) {
1069 return format;
1070 }
1071 }
1072
1073 throw runtime_error("failed to find supported format!");
1074 }
1075
1076 bool hasStencilComponent(VkFormat format) {
1077 return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
1078 }
1079
1080 void createImageResources(string filename, VkImage& image, VkDeviceMemory& imageMemory, VkImageView& view) {
1081 int texWidth, texHeight, texChannels;
1082
1083 stbi_uc* pixels = stbi_load(filename.c_str(), &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
1084 VkDeviceSize imageSize = texWidth * texHeight * 4;
1085
1086 if (!pixels) {
1087 throw runtime_error("failed to load texture image!");
1088 }
1089
1090 VkBuffer stagingBuffer;
1091 VkDeviceMemory stagingBufferMemory;
1092
1093 createBuffer(imageSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
1094 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
1095 stagingBuffer, stagingBufferMemory);
1096
1097 void* data;
1098
1099 vkMapMemory(device, stagingBufferMemory, 0, imageSize, 0, &data);
1100 memcpy(data, pixels, static_cast<size_t>(imageSize));
1101 vkUnmapMemory(device, stagingBufferMemory);
1102
1103 stbi_image_free(pixels);
1104
1105 createImage(texWidth, texHeight, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL,
1106 VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
1107 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, image, imageMemory);
1108
1109 transitionImageLayout(image, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
1110 copyBufferToImage(stagingBuffer, image, static_cast<uint32_t>(texWidth), static_cast<uint32_t>(texHeight));
1111 transitionImageLayout(image, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
1112
1113 vkDestroyBuffer(device, stagingBuffer, nullptr);
1114 vkFreeMemory(device, stagingBufferMemory, nullptr);
1115
1116 view = createImageView(image, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT);
1117 }
1118
1119 void createImageResourcesFromSDLTexture(SDL_Texture* texture, VkImage& image, VkDeviceMemory& imageMemory, VkImageView& view) {
1120 int a, w, h;
1121
1122 // I only need this here for the width and height, which are constants, so just use those instead
1123 SDL_QueryTexture(texture, nullptr, &a, &w, &h);
1124
1125 createImage(w, h, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL,
1126 VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
1127 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, image, imageMemory);
1128
1129 view = createImageView(image, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_ASPECT_COLOR_BIT);
1130 }
1131
1132 void populateImageFromSDLTexture(SDL_Texture* texture, VkImage& image) {
1133 int a, w, h, pitch;
1134
1135 SDL_QueryTexture(texture, nullptr, &a, &w, &h);
1136
1137 VkDeviceSize imageSize = w * h * 4;
1138 unsigned char* pixels = new unsigned char[imageSize];
1139
1140 SDL_RenderReadPixels(gRenderer, nullptr, SDL_PIXELFORMAT_ABGR8888, pixels, w * 4);
1141
1142 VkBuffer stagingBuffer;
1143 VkDeviceMemory stagingBufferMemory;
1144
1145 createBuffer(imageSize,
1146 VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
1147 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
1148 stagingBuffer, stagingBufferMemory);
1149
1150 void* data;
1151
1152 vkMapMemory(device, stagingBufferMemory, 0, VK_WHOLE_SIZE, 0, &data);
1153 memcpy(data, pixels, static_cast<size_t>(imageSize));
1154
1155 VkMappedMemoryRange mappedMemoryRange = {};
1156 mappedMemoryRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
1157 mappedMemoryRange.memory = stagingBufferMemory;
1158 mappedMemoryRange.offset = 0;
1159 mappedMemoryRange.size = VK_WHOLE_SIZE;
1160
1161 // TODO: Should probably check that the function succeeded
1162 vkFlushMappedMemoryRanges(device, 1, &mappedMemoryRange);
1163
1164 vkUnmapMemory(device, stagingBufferMemory);
1165
1166 transitionImageLayout(image, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
1167 copyBufferToImage(stagingBuffer, image, static_cast<uint32_t>(w), static_cast<uint32_t>(h));
1168 transitionImageLayout(image, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
1169
1170 vkDestroyBuffer(device, stagingBuffer, nullptr);
1171 vkFreeMemory(device, stagingBufferMemory, nullptr);
1172 }
1173
1174 void createImage(uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage,
1175 VkMemoryPropertyFlags properties, VkImage& image, VkDeviceMemory& imageMemory) {
1176 VkImageCreateInfo imageInfo = {};
1177 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1178 imageInfo.imageType = VK_IMAGE_TYPE_2D;
1179 imageInfo.extent.width = width;
1180 imageInfo.extent.height = height;
1181 imageInfo.extent.depth = 1;
1182 imageInfo.mipLevels = 1;
1183 imageInfo.arrayLayers = 1;
1184 imageInfo.format = format;
1185 imageInfo.tiling = tiling;
1186 imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1187 imageInfo.usage = usage;
1188 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
1189 imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1190
1191 if (vkCreateImage(device, &imageInfo, nullptr, &image) != VK_SUCCESS) {
1192 throw runtime_error("failed to create image!");
1193 }
1194
1195 VkMemoryRequirements memRequirements;
1196 vkGetImageMemoryRequirements(device, image, &memRequirements);
1197
1198 VkMemoryAllocateInfo allocInfo = {};
1199 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1200 allocInfo.allocationSize = memRequirements.size;
1201 allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);
1202
1203 if (vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory) != VK_SUCCESS) {
1204 throw runtime_error("failed to allocate image memory!");
1205 }
1206
1207 vkBindImageMemory(device, image, imageMemory, 0);
1208 }
1209
1210 void transitionImageLayout(VkImage image, VkFormat format, VkImageLayout oldLayout, VkImageLayout newLayout) {
1211 VkCommandBuffer commandBuffer = beginSingleTimeCommands();
1212
1213 VkImageMemoryBarrier barrier = {};
1214 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
1215 barrier.oldLayout = oldLayout;
1216 barrier.newLayout = newLayout;
1217 barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1218 barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
1219 barrier.image = image;
1220
1221 if (newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
1222 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
1223
1224 if (hasStencilComponent(format)) {
1225 barrier.subresourceRange.aspectMask |= VK_IMAGE_ASPECT_STENCIL_BIT;
1226 }
1227 } else {
1228 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1229 }
1230
1231 barrier.subresourceRange.baseMipLevel = 0;
1232 barrier.subresourceRange.levelCount = 1;
1233 barrier.subresourceRange.baseArrayLayer = 0;
1234 barrier.subresourceRange.layerCount = 1;
1235
1236 VkPipelineStageFlags sourceStage;
1237 VkPipelineStageFlags destinationStage;
1238
1239 if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) {
1240 barrier.srcAccessMask = 0;
1241 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1242
1243 sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
1244 destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
1245 } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
1246 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
1247 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
1248
1249 sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
1250 destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
1251 } else if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
1252 barrier.srcAccessMask = 0;
1253 barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
1254
1255 sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
1256 destinationStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
1257 } else {
1258 throw invalid_argument("unsupported layout transition!");
1259 }
1260
1261 vkCmdPipelineBarrier(
1262 commandBuffer,
1263 sourceStage, destinationStage,
1264 0,
1265 0, nullptr,
1266 0, nullptr,
1267 1, &barrier
1268 );
1269
1270 endSingleTimeCommands(commandBuffer);
1271 }
1272
1273 void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) {
1274 VkCommandBuffer commandBuffer = beginSingleTimeCommands();
1275
1276 VkBufferImageCopy region = {};
1277 region.bufferOffset = 0;
1278 region.bufferRowLength = 0;
1279 region.bufferImageHeight = 0;
1280 region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1281 region.imageSubresource.mipLevel = 0;
1282 region.imageSubresource.baseArrayLayer = 0;
1283 region.imageSubresource.layerCount = 1;
1284 region.imageOffset = { 0, 0, 0 };
1285 region.imageExtent = { width, height, 1 };
1286
1287 vkCmdCopyBufferToImage(
1288 commandBuffer,
1289 buffer,
1290 image,
1291 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1292 1,
1293 &region
1294 );
1295
1296 endSingleTimeCommands(commandBuffer);
1297 }
1298
1299 VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags) {
1300 VkImageViewCreateInfo viewInfo = {};
1301 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1302 viewInfo.image = image;
1303 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1304 viewInfo.format = format;
1305
1306 viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
1307 viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
1308 viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
1309 viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
1310
1311 viewInfo.subresourceRange.aspectMask = aspectFlags;
1312 viewInfo.subresourceRange.baseMipLevel = 0;
1313 viewInfo.subresourceRange.levelCount = 1;
1314 viewInfo.subresourceRange.baseArrayLayer = 0;
1315 viewInfo.subresourceRange.layerCount = 1;
1316
1317 VkImageView imageView;
1318 if (vkCreateImageView(device, &viewInfo, nullptr, &imageView) != VK_SUCCESS) {
1319 throw runtime_error("failed to create texture image view!");
1320 }
1321
1322 return imageView;
1323 }
1324
1325 void createTextureSampler() {
1326 VkSamplerCreateInfo samplerInfo = {};
1327 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
1328 samplerInfo.magFilter = VK_FILTER_LINEAR;
1329 samplerInfo.minFilter = VK_FILTER_LINEAR;
1330
1331 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1332 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1333 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
1334
1335 samplerInfo.anisotropyEnable = VK_TRUE;
1336 samplerInfo.maxAnisotropy = 16;
1337 samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
1338 samplerInfo.unnormalizedCoordinates = VK_FALSE;
1339 samplerInfo.compareEnable = VK_FALSE;
1340 samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
1341 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
1342 samplerInfo.mipLodBias = 0.0f;
1343 samplerInfo.minLod = 0.0f;
1344 samplerInfo.maxLod = 0.0f;
1345
1346 if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
1347 throw runtime_error("failed to create texture sampler!");
1348 }
1349 }
1350
1351 void createShaderBuffers(GraphicsPipelineInfo& info, const vector<Vertex>& vertices, const vector<uint16_t>& indices) {
1352 createVertexBuffer(info.vertexBuffer, info.vertexBufferMemory, vertices);
1353 info.numVertices = vertices.size();
1354
1355 createIndexBuffer(info.indexBuffer, info.indexBufferMemory, indices);
1356 info.numIndices = indices.size();
1357 }
1358
1359 void createVertexBuffer(VkBuffer& vertexBuffer, VkDeviceMemory& vertexBufferMemory,
1360 const vector<Vertex>& vertices) {
1361 VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size();
1362
1363 VkBuffer stagingBuffer;
1364 VkDeviceMemory stagingBufferMemory;
1365 createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
1366 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
1367 stagingBuffer, stagingBufferMemory);
1368
1369 void* data;
1370 vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data);
1371 memcpy(data, vertices.data(), (size_t) bufferSize);
1372 vkUnmapMemory(device, stagingBufferMemory);
1373
1374 createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
1375 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory);
1376
1377 copyBuffer(stagingBuffer, vertexBuffer, bufferSize);
1378
1379 vkDestroyBuffer(device, stagingBuffer, nullptr);
1380 vkFreeMemory(device, stagingBufferMemory, nullptr);
1381 }
1382
1383 void createIndexBuffer(VkBuffer& indexBuffer, VkDeviceMemory& indexBufferMemory,
1384 const vector<uint16_t>& indices) {
1385 VkDeviceSize bufferSize = sizeof(indices[0]) * indices.size();
1386
1387 VkBuffer stagingBuffer;
1388 VkDeviceMemory stagingBufferMemory;
1389 createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
1390 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
1391 stagingBuffer, stagingBufferMemory);
1392
1393 void* data;
1394 vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data);
1395 memcpy(data, indices.data(), (size_t) bufferSize);
1396 vkUnmapMemory(device, stagingBufferMemory);
1397
1398 createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
1399 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, indexBuffer, indexBufferMemory);
1400
1401 copyBuffer(stagingBuffer, indexBuffer, bufferSize);
1402
1403 vkDestroyBuffer(device, stagingBuffer, nullptr);
1404 vkFreeMemory(device, stagingBufferMemory, nullptr);
1405 }
1406
1407 void createUniformBuffers() {
1408 VkDeviceSize bufferSize = sizeof(UniformBufferObject);
1409
1410 uniformBuffers.resize(swapChainImages.size());
1411 uniformBuffersMemory.resize(swapChainImages.size());
1412
1413 for (size_t i = 0; i < swapChainImages.size(); i++) {
1414 createBuffer(bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
1415 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
1416 uniformBuffers[i], uniformBuffersMemory[i]);
1417 }
1418 }
1419
1420 void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) {
1421 VkBufferCreateInfo bufferInfo = {};
1422 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
1423 bufferInfo.size = size;
1424 bufferInfo.usage = usage;
1425 bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1426
1427 if (vkCreateBuffer(device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) {
1428 throw runtime_error("failed to create buffer!");
1429 }
1430
1431 VkMemoryRequirements memRequirements;
1432 vkGetBufferMemoryRequirements(device, buffer, &memRequirements);
1433
1434 VkMemoryAllocateInfo allocInfo = {};
1435 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1436 allocInfo.allocationSize = memRequirements.size;
1437 allocInfo.memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties);
1438
1439 if (vkAllocateMemory(device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) {
1440 throw runtime_error("failed to allocate buffer memory!");
1441 }
1442
1443 vkBindBufferMemory(device, buffer, bufferMemory, 0);
1444 }
1445
1446 void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size) {
1447 VkCommandBuffer commandBuffer = beginSingleTimeCommands();
1448
1449 VkBufferCopy copyRegion = {};
1450 copyRegion.size = size;
1451 vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, &copyRegion);
1452
1453 endSingleTimeCommands(commandBuffer);
1454 }
1455
1456 VkCommandBuffer beginSingleTimeCommands() {
1457 VkCommandBufferAllocateInfo allocInfo = {};
1458 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1459 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1460 allocInfo.commandPool = commandPool;
1461 allocInfo.commandBufferCount = 1;
1462
1463 VkCommandBuffer commandBuffer;
1464 vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer);
1465
1466 VkCommandBufferBeginInfo beginInfo = {};
1467 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1468 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
1469
1470 vkBeginCommandBuffer(commandBuffer, &beginInfo);
1471
1472 return commandBuffer;
1473 }
1474
1475 void endSingleTimeCommands(VkCommandBuffer commandBuffer) {
1476 vkEndCommandBuffer(commandBuffer);
1477
1478 VkSubmitInfo submitInfo = {};
1479 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1480 submitInfo.commandBufferCount = 1;
1481 submitInfo.pCommandBuffers = &commandBuffer;
1482
1483 vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
1484 vkQueueWaitIdle(graphicsQueue);
1485
1486 vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
1487 }
1488
1489 uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) {
1490 VkPhysicalDeviceMemoryProperties memProperties;
1491 vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
1492
1493 for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
1494 if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
1495 return i;
1496 }
1497 }
1498
1499 throw runtime_error("failed to find suitable memory type!");
1500 }
1501
1502 void createDescriptorPool(GraphicsPipelineInfo& info) {
1503 array<VkDescriptorPoolSize, 3> poolSizes = {};
1504 poolSizes[0].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1505 poolSizes[0].descriptorCount = static_cast<uint32_t>(swapChainImages.size());
1506 poolSizes[1].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1507 poolSizes[1].descriptorCount = static_cast<uint32_t>(swapChainImages.size());
1508 poolSizes[2].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1509 poolSizes[2].descriptorCount = static_cast<uint32_t>(swapChainImages.size());
1510
1511 VkDescriptorPoolCreateInfo poolInfo = {};
1512 poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
1513 poolInfo.poolSizeCount = static_cast<uint32_t>(poolSizes.size());
1514 poolInfo.pPoolSizes = poolSizes.data();
1515 poolInfo.maxSets = static_cast<uint32_t>(swapChainImages.size());
1516
1517 if (vkCreateDescriptorPool(device, &poolInfo, nullptr, &info.descriptorPool) != VK_SUCCESS) {
1518 throw runtime_error("failed to create descriptor pool!");
1519 }
1520 }
1521
1522 void createDescriptorSets(GraphicsPipelineInfo& info) {
1523 vector<VkDescriptorSetLayout> layouts(swapChainImages.size(), info.descriptorSetLayout);
1524
1525 VkDescriptorSetAllocateInfo allocInfo = {};
1526 allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
1527 allocInfo.descriptorPool = info.descriptorPool;
1528 allocInfo.descriptorSetCount = static_cast<uint32_t>(swapChainImages.size());
1529 allocInfo.pSetLayouts = layouts.data();
1530
1531 info.descriptorSets.resize(swapChainImages.size());
1532 if (vkAllocateDescriptorSets(device, &allocInfo, info.descriptorSets.data()) != VK_SUCCESS) {
1533 throw runtime_error("failed to allocate descriptor sets!");
1534 }
1535
1536 for (size_t i = 0; i < swapChainImages.size(); i++) {
1537 VkDescriptorBufferInfo bufferInfo = {};
1538 bufferInfo.buffer = uniformBuffers[i];
1539 bufferInfo.offset = 0;
1540 bufferInfo.range = sizeof(UniformBufferObject);
1541
1542 VkDescriptorImageInfo imageInfo = {};
1543 imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1544 imageInfo.imageView = textureImageView;
1545 imageInfo.sampler = textureSampler;
1546
1547 VkDescriptorImageInfo overlayImageInfo = {};
1548 overlayImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
1549 overlayImageInfo.imageView = sdlOverlayImageView;
1550 overlayImageInfo.sampler = textureSampler;
1551
1552 array<VkWriteDescriptorSet, 3> descriptorWrites = {};
1553
1554 descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1555 descriptorWrites[0].dstSet = info.descriptorSets[i];
1556 descriptorWrites[0].dstBinding = 0;
1557 descriptorWrites[0].dstArrayElement = 0;
1558 descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
1559 descriptorWrites[0].descriptorCount = 1;
1560 descriptorWrites[0].pBufferInfo = &bufferInfo;
1561 descriptorWrites[0].pImageInfo = nullptr;
1562 descriptorWrites[0].pTexelBufferView = nullptr;
1563
1564 descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1565 descriptorWrites[1].dstSet = info.descriptorSets[i];
1566 descriptorWrites[1].dstBinding = 1;
1567 descriptorWrites[1].dstArrayElement = 0;
1568 descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1569 descriptorWrites[1].descriptorCount = 1;
1570 descriptorWrites[1].pBufferInfo = nullptr;
1571 descriptorWrites[1].pImageInfo = &imageInfo;
1572 descriptorWrites[1].pTexelBufferView = nullptr;
1573
1574 descriptorWrites[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1575 descriptorWrites[2].dstSet = info.descriptorSets[i];
1576 descriptorWrites[2].dstBinding = 2;
1577 descriptorWrites[2].dstArrayElement = 0;
1578 descriptorWrites[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
1579 descriptorWrites[2].descriptorCount = 1;
1580 descriptorWrites[2].pBufferInfo = nullptr;
1581 descriptorWrites[2].pImageInfo = &overlayImageInfo;
1582 descriptorWrites[2].pTexelBufferView = nullptr;
1583
1584 vkUpdateDescriptorSets(device, static_cast<uint32_t>(descriptorWrites.size()), descriptorWrites.data(), 0, nullptr);
1585 }
1586 }
1587
1588 void createCommandBuffers() {
1589 commandBuffers.resize(swapChainFramebuffers.size());
1590
1591 VkCommandBufferAllocateInfo allocInfo = {};
1592 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1593 allocInfo.commandPool = commandPool;
1594 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1595 allocInfo.commandBufferCount = (uint32_t) commandBuffers.size();
1596
1597 if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
1598 throw runtime_error("failed to allocate command buffers!");
1599 }
1600
1601 for (size_t i = 0; i < commandBuffers.size(); i++) {
1602 VkCommandBufferBeginInfo beginInfo = {};
1603 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1604 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
1605 beginInfo.pInheritanceInfo = nullptr;
1606
1607 if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
1608 throw runtime_error("failed to begin recording command buffer!");
1609 }
1610
1611 VkRenderPassBeginInfo renderPassInfo = {};
1612 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
1613 renderPassInfo.renderPass = renderPass;
1614 renderPassInfo.framebuffer = swapChainFramebuffers[i];
1615 renderPassInfo.renderArea.offset = { 0, 0 };
1616 renderPassInfo.renderArea.extent = swapChainExtent;
1617
1618 array<VkClearValue, 2> clearValues = {};
1619 clearValues[0].color = { 0.0f, 0.0f, 0.0f, 1.0f };
1620 clearValues[1].depthStencil = { 1.0f, 0 };
1621
1622 renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
1623 renderPassInfo.pClearValues = clearValues.data();
1624
1625 vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
1626
1627 createGraphicsPipelineCommands(scenePipeline, i);
1628 createGraphicsPipelineCommands(overlayPipeline, i);
1629
1630 vkCmdEndRenderPass(commandBuffers[i]);
1631
1632 if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
1633 throw runtime_error("failed to record command buffer!");
1634 }
1635 }
1636 }
1637
1638 void createGraphicsPipelineCommands(GraphicsPipelineInfo& info, uint32_t currentImage) {
1639 vkCmdBindPipeline(commandBuffers[currentImage], VK_PIPELINE_BIND_POINT_GRAPHICS, info.pipeline);
1640 vkCmdBindDescriptorSets(commandBuffers[currentImage], VK_PIPELINE_BIND_POINT_GRAPHICS, info.pipelineLayout, 0, 1,
1641 &info.descriptorSets[currentImage], 0, nullptr);
1642
1643 VkBuffer vertexBuffers[] = { info.vertexBuffer };
1644 VkDeviceSize offsets[] = { 0 };
1645 vkCmdBindVertexBuffers(commandBuffers[currentImage], 0, 1, vertexBuffers, offsets);
1646
1647 vkCmdBindIndexBuffer(commandBuffers[currentImage], info.indexBuffer, 0, VK_INDEX_TYPE_UINT16);
1648
1649 vkCmdDrawIndexed(commandBuffers[currentImage], static_cast<uint32_t>(info.numIndices), 1, 0, 0, 0);
1650 }
1651
1652 void createSyncObjects() {
1653 imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
1654 renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
1655 inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
1656
1657 VkSemaphoreCreateInfo semaphoreInfo = {};
1658 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1659
1660 VkFenceCreateInfo fenceInfo = {};
1661 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1662 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
1663
1664 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
1665 if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS ||
1666 vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS ||
1667 vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {
1668 throw runtime_error("failed to create synchronization objects for a frame!");
1669 }
1670 }
1671 }
1672
1673 void mainLoop() {
1674 // TODO: Create some generic event-handling functions in game-gui-*
1675 SDL_Event e;
1676 bool quit = false;
1677
1678 while (!quit) {
1679 while (SDL_PollEvent(&e)) {
1680 if (e.type == SDL_QUIT) {
1681 quit = true;
1682 }
1683 if (e.type == SDL_KEYDOWN) {
1684 quit = true;
1685 }
1686 if (e.type == SDL_MOUSEBUTTONDOWN) {
1687 quit = true;
1688 }
1689 if (e.type == SDL_WINDOWEVENT) {
1690 if (e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED ||
1691 e.window.event == SDL_WINDOWEVENT_MINIMIZED) {
1692 framebufferResized = true;
1693 }
1694 }
1695 }
1696
1697 drawUI();
1698
1699 drawFrame();
1700 }
1701
1702 vkDeviceWaitIdle(device);
1703 }
1704
1705 void drawFrame() {
1706 vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, numeric_limits<uint64_t>::max());
1707
1708 uint32_t imageIndex;
1709
1710 VkResult result = vkAcquireNextImageKHR(device, swapChain, numeric_limits<uint64_t>::max(),
1711 imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
1712
1713 if (result == VK_ERROR_OUT_OF_DATE_KHR) {
1714 recreateSwapChain();
1715 return;
1716 } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
1717 throw runtime_error("failed to acquire swap chain image!");
1718 }
1719
1720 updateUniformBuffer(imageIndex);
1721
1722 VkSubmitInfo submitInfo = {};
1723 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1724
1725 VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] };
1726 VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
1727
1728 submitInfo.waitSemaphoreCount = 1;
1729 submitInfo.pWaitSemaphores = waitSemaphores;
1730 submitInfo.pWaitDstStageMask = waitStages;
1731 submitInfo.commandBufferCount = 1;
1732 submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
1733
1734 VkSemaphore signalSemaphores[] = { renderFinishedSemaphores[currentFrame] };
1735
1736 submitInfo.signalSemaphoreCount = 1;
1737 submitInfo.pSignalSemaphores = signalSemaphores;
1738
1739 vkResetFences(device, 1, &inFlightFences[currentFrame]);
1740
1741 if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
1742 throw runtime_error("failed to submit draw command buffer!");
1743 }
1744
1745 VkPresentInfoKHR presentInfo = {};
1746 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
1747 presentInfo.waitSemaphoreCount = 1;
1748 presentInfo.pWaitSemaphores = signalSemaphores;
1749
1750 VkSwapchainKHR swapChains[] = { swapChain };
1751 presentInfo.swapchainCount = 1;
1752 presentInfo.pSwapchains = swapChains;
1753 presentInfo.pImageIndices = &imageIndex;
1754 presentInfo.pResults = nullptr;
1755
1756 result = vkQueuePresentKHR(presentQueue, &presentInfo);
1757
1758 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {
1759 framebufferResized = false;
1760 recreateSwapChain();
1761 } else if (result != VK_SUCCESS) {
1762 throw runtime_error("failed to present swap chain image!");
1763 }
1764
1765 currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
1766 currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
1767 }
1768
1769 void drawUI() {
1770 // TODO: Since I currently don't use any other render targets,
1771 // I may as well set this once before the render loop
1772 SDL_SetRenderTarget(gRenderer, uiOverlay);
1773
1774 SDL_SetRenderDrawColor(gRenderer, 0x00, 0x00, 0x00, 0x00);
1775 SDL_RenderClear(gRenderer);
1776
1777 SDL_Rect rect;
1778
1779 rect = {280, 220, 100, 100};
1780 SDL_SetRenderDrawColor(gRenderer, 0x00, 0xFF, 0x00, 0xFF);
1781 SDL_RenderFillRect(gRenderer, &rect);
1782 SDL_SetRenderDrawColor(gRenderer, 0x00, 0x9F, 0x9F, 0xFF);
1783
1784 rect = {10, 10, 0, 0};
1785 SDL_QueryTexture(uiText, nullptr, nullptr, &(rect.w), &(rect.h));
1786 SDL_RenderCopy(gRenderer, uiText, nullptr, &rect);
1787
1788 rect = {10, 80, 0, 0};
1789 SDL_QueryTexture(uiImage, nullptr, nullptr, &(rect.w), &(rect.h));
1790 SDL_RenderCopy(gRenderer, uiImage, nullptr, &rect);
1791
1792 SDL_SetRenderDrawColor(gRenderer, 0x00, 0x00, 0xFF, 0xFF);
1793 SDL_RenderDrawLine(gRenderer, 50, 5, 150, 500);
1794
1795 populateImageFromSDLTexture(uiOverlay, sdlOverlayImage);
1796 }
1797
1798 void updateUniformBuffer(uint32_t currentImage) {
1799 static auto startTime = chrono::high_resolution_clock::now();
1800
1801 auto currentTime = chrono::high_resolution_clock::now();
1802 float time = chrono::duration<float, chrono::seconds::period>(currentTime - startTime).count();
1803
1804 UniformBufferObject ubo = {};
1805 ubo.model = glm::rotate(glm::mat4(1.0f), time * glm::radians(90.0f), glm::vec3(0.0f, 0.0f, 1.0f));
1806 ubo.view = glm::lookAt(glm::vec3(0.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
1807 ubo.proj = glm::perspective(glm::radians(45.0f), swapChainExtent.width / (float)swapChainExtent.height, 0.1f, 10.0f);
1808 ubo.proj[1][1] *= -1; // flip the y-axis so that +y is up
1809
1810 void* data;
1811 vkMapMemory(device, uniformBuffersMemory[currentImage], 0, sizeof(ubo), 0, &data);
1812 memcpy(data, &ubo, sizeof(ubo));
1813 vkUnmapMemory(device, uniformBuffersMemory[currentImage]);
1814 }
1815
1816 void recreateSwapChain() {
1817 int width = 0, height = 0;
1818
1819 gui->GetWindowSize(&width, &height);
1820
1821 while (width == 0 || height == 0 ||
1822 (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) != 0) {
1823 SDL_WaitEvent(nullptr);
1824 gui->GetWindowSize(&width, &height);
1825 }
1826
1827 vkDeviceWaitIdle(device);
1828
1829 cleanupSwapChain();
1830
1831 createSwapChain();
1832 createImageViews();
1833 createRenderPass();
1834
1835 createGraphicsPipeline("shaders/vert.spv", "shaders/frag.spv", scenePipeline);
1836 createGraphicsPipeline("shaders/vert.spv", "shaders/frag.spv", overlayPipeline);
1837
1838 createDepthResources();
1839 createFramebuffers();
1840 createUniformBuffers();
1841
1842 createDescriptorPool(scenePipeline);
1843 createDescriptorSets(scenePipeline);
1844
1845 createDescriptorPool(overlayPipeline);
1846 createDescriptorSets(overlayPipeline);
1847
1848 createCommandBuffers();
1849 }
1850
1851 void cleanup() {
1852 cleanupSwapChain();
1853
1854 vkDestroySampler(device, textureSampler, nullptr);
1855
1856 vkDestroyImageView(device, textureImageView, nullptr);
1857 vkDestroyImage(device, textureImage, nullptr);
1858 vkFreeMemory(device, textureImageMemory, nullptr);
1859
1860 vkDestroyImageView(device, overlayImageView, nullptr);
1861 vkDestroyImage(device, overlayImage, nullptr);
1862 vkFreeMemory(device, overlayImageMemory, nullptr);
1863
1864 vkDestroyImageView(device, sdlOverlayImageView, nullptr);
1865 vkDestroyImage(device, sdlOverlayImage, nullptr);
1866 vkFreeMemory(device, sdlOverlayImageMemory, nullptr);
1867
1868 cleanupPipelineBuffers(scenePipeline);
1869 cleanupPipelineBuffers(overlayPipeline);
1870
1871 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
1872 vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
1873 vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
1874 vkDestroyFence(device, inFlightFences[i], nullptr);
1875 }
1876
1877 vkDestroyCommandPool(device, commandPool, nullptr);
1878
1879 vkDestroyDevice(device, nullptr);
1880
1881 if (enableValidationLayers) {
1882 DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
1883 }
1884
1885 vkDestroySurfaceKHR(instance, surface, nullptr);
1886 vkDestroyInstance(instance, nullptr);
1887
1888 // TODO: Check if any of these functions accept null parameters
1889 // If they do, I don't need to check for that
1890
1891 if (uiOverlay != nullptr) {
1892 SDL_DestroyTexture(uiOverlay);
1893 uiOverlay = nullptr;
1894 }
1895
1896 TTF_CloseFont(gFont);
1897 gFont = nullptr;
1898
1899 if (uiText != nullptr) {
1900 SDL_DestroyTexture(uiText);
1901 uiText = nullptr;
1902 }
1903
1904 if (uiImage != nullptr) {
1905 SDL_DestroyTexture(uiImage);
1906 uiImage = nullptr;
1907 }
1908
1909 SDL_DestroyRenderer(gRenderer);
1910 gRenderer = nullptr;
1911
1912 gui->DestroyWindow();
1913 gui->Shutdown();
1914 delete gui;
1915 }
1916
1917 void cleanupSwapChain() {
1918 vkDestroyImageView(device, depthImageView, nullptr);
1919 vkDestroyImage(device, depthImage, nullptr);
1920 vkFreeMemory(device, depthImageMemory, nullptr);
1921
1922 for (auto framebuffer : swapChainFramebuffers) {
1923 vkDestroyFramebuffer(device, framebuffer, nullptr);
1924 }
1925
1926 vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
1927
1928 cleanupPipeline(scenePipeline);
1929 cleanupPipeline(overlayPipeline);
1930
1931 vkDestroyRenderPass(device, renderPass, nullptr);
1932
1933 for (auto imageView : swapChainImageViews) {
1934 vkDestroyImageView(device, imageView, nullptr);
1935 }
1936
1937 vkDestroySwapchainKHR(device, swapChain, nullptr);
1938
1939 for (size_t i = 0; i < swapChainImages.size(); i++) {
1940 vkDestroyBuffer(device, uniformBuffers[i], nullptr);
1941 vkFreeMemory(device, uniformBuffersMemory[i], nullptr);
1942 }
1943 }
1944
1945 void cleanupPipeline(GraphicsPipelineInfo& pipeline) {
1946 vkDestroyPipeline(device, pipeline.pipeline, nullptr);
1947 vkDestroyDescriptorPool(device, pipeline.descriptorPool, nullptr);
1948 vkDestroyPipelineLayout(device, pipeline.pipelineLayout, nullptr);
1949 }
1950
1951 void cleanupPipelineBuffers(GraphicsPipelineInfo& pipeline) {
1952 vkDestroyDescriptorSetLayout(device, pipeline.descriptorSetLayout, nullptr);
1953
1954 vkDestroyBuffer(device, pipeline.vertexBuffer, nullptr);
1955 vkFreeMemory(device, pipeline.vertexBufferMemory, nullptr);
1956 vkDestroyBuffer(device, pipeline.indexBuffer, nullptr);
1957 vkFreeMemory(device, pipeline.indexBufferMemory, nullptr);
1958 }
1959
1960 static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
1961 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
1962 VkDebugUtilsMessageTypeFlagsEXT messageType,
1963 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
1964 void* pUserData) {
1965 cerr << "validation layer: " << pCallbackData->pMessage << endl;
1966
1967 return VK_FALSE;
1968 }
1969
1970 static vector<char> readFile(const string& filename) {
1971 ifstream file(filename, ios::ate | ios::binary);
1972
1973 if (!file.is_open()) {
1974 throw runtime_error("failed to open file!");
1975 }
1976
1977 size_t fileSize = (size_t) file.tellg();
1978 vector<char> buffer(fileSize);
1979
1980 file.seekg(0);
1981 file.read(buffer.data(), fileSize);
1982
1983 file.close();
1984
1985 return buffer;
1986 }
1987};
1988
1989int main(int argc, char* argv[]) {
1990
1991#ifdef NDEBUG
1992 cout << "DEBUGGING IS OFF" << endl;
1993#else
1994 cout << "DEBUGGING IS ON" << endl;
1995#endif
1996
1997 cout << "Starting Vulkan game..." << endl;
1998
1999 VulkanGame game;
2000
2001 try {
2002 game.run();
2003 } catch (const exception& e) {
2004 cerr << e.what() << endl;
2005 return EXIT_FAILURE;
2006 }
2007
2008 cout << "Finished running the game" << endl;
2009
2010 return EXIT_SUCCESS;
2011}
Note: See TracBrowser for help on using the repository browser.