source: opengl-game/vulkan-game.cpp@ 4f63fa8

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

Fix the MVP matrix

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