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

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

Create a descriptor pool and descriptor sets for the mvp matrix ubo, to successfully render a rotating square

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