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

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

Refactor the code to allow different graphics pipelines to use different Vertex structs and different uniform and vertex attributes

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