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

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

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

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