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

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

Enable rendering of geometry using multiple sets of vertex and index buffers and create a BufferInfo object to store the Vulkan objects and data related to each set

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