source: opengl-game/vulkan-game.cpp@ 5f3dba8

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

Create a transparent texture in SDL and render some sample images and shapes onto it

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