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

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

Create a Vulkan image and fill it with image data loaded from a file

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