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

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

Create the overlay texture using different flags based on the SDL version (2.0.9 vs 2.0.10) and change compile.sh to compile all .vert and .frag files inside the shaders folder

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