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

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

Create a ubo and update it with the MVP matrix every frame

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