source: opengl-game/vulkan-game.cpp@ 5936c58

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

Allow parts of the UI overlay to be transpare, so that the 3D scene is visible behind it

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