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

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

Add a depth buffer for depth testing

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