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

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

Add separate shaders for the scene and overlay to the Vulkan project and add some notes about future work

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