source: opengl-game/vulkan-game.cpp@ 621664a

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

Reformat vulkan-game.cpp and fix a seg fault

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