source: opengl-game/vulkan-ref.cpp@ e83b155

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

In vulkangame, destroy the texture sampler, all the uniform buffer resources, and all the resources for each Vulkan image before terminating the program

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