source: opengl-game/vulkan-game.cpp@ 75108ef

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

Enable and detect window resizing and recreate the swap chain when it becomes outdated, including when the window is resized

  • Property mode set to 100644
File size: 40.2 KB
Line 
1#include <vulkan/vulkan.h>
2
3#include <SDL2/SDL.h>
4#include <SDL2/SDL_vulkan.h>
5
6//#define _USE_MATH_DEFINES // Will be needed when/if I need to # include <cmath>
7
8#define GLM_FORCE_RADIANS
9#define GLM_FORCE_DEPTH_ZERO_TO_ONE
10#include <glm/vec4.hpp>
11#include <glm/mat4x4.hpp>
12
13#include <iostream>
14#include <vector>
15#include <set>
16#include <stdexcept>
17#include <cstdlib>
18#include <optional>
19#include <algorithm>
20#include <fstream>
21
22#include "game-gui-sdl.hpp"
23
24using namespace std;
25//using namespace glm;
26
27const int SCREEN_WIDTH = 800;
28const int SCREEN_HEIGHT = 600;
29
30const int MAX_FRAMES_IN_FLIGHT = 2;
31
32#ifdef NDEBUG
33 const bool enableValidationLayers = false;
34#else
35 const bool enableValidationLayers = true;
36#endif
37
38const vector<const char*> validationLayers = {
39 "VK_LAYER_KHRONOS_validation"
40};
41
42const vector<const char*> deviceExtensions = {
43 VK_KHR_SWAPCHAIN_EXTENSION_NAME
44};
45
46struct QueueFamilyIndices {
47 optional<uint32_t> graphicsFamily;
48 optional<uint32_t> presentFamily;
49
50 bool isComplete() {
51 return graphicsFamily.has_value() && presentFamily.has_value();
52 }
53};
54
55struct SwapChainSupportDetails {
56 VkSurfaceCapabilitiesKHR capabilities;
57 vector<VkSurfaceFormatKHR> formats;
58 vector<VkPresentModeKHR> presentModes;
59};
60
61VkResult CreateDebugUtilsMessengerEXT(VkInstance instance,
62 const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
63 const VkAllocationCallbacks* pAllocator,
64 VkDebugUtilsMessengerEXT* pDebugMessenger) {
65 auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(
66 instance, "vkCreateDebugUtilsMessengerEXT");
67
68 if (func != nullptr) {
69 return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
70 } else {
71 return VK_ERROR_EXTENSION_NOT_PRESENT;
72 }
73}
74
75void DestroyDebugUtilsMessengerEXT(VkInstance instance,
76 VkDebugUtilsMessengerEXT debugMessenger,
77 const VkAllocationCallbacks* pAllocator) {
78 auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(
79 instance, "vkDestroyDebugUtilsMessengerEXT");
80
81 if (func != nullptr) {
82 func(instance, debugMessenger, pAllocator);
83 }
84}
85
86class VulkanGame {
87 public:
88 void run() {
89 if (initWindow() == RTWO_ERROR) {
90 return;
91 }
92 initVulkan();
93 mainLoop();
94 cleanup();
95 }
96 private:
97 GameGui* gui = new GameGui_SDL();
98 SDL_Window* window = nullptr;
99
100 VkInstance instance;
101 VkDebugUtilsMessengerEXT debugMessenger;
102 VkSurfaceKHR surface;
103 SDL_Surface* sdlSurface = nullptr;
104
105 VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
106 VkDevice device;
107
108 VkQueue graphicsQueue;
109 VkQueue presentQueue;
110
111 VkSwapchainKHR swapChain;
112 vector<VkImage> swapChainImages;
113 VkFormat swapChainImageFormat;
114 VkExtent2D swapChainExtent;
115
116 vector<VkImageView> swapChainImageViews;
117 VkRenderPass renderPass;
118 VkPipelineLayout pipelineLayout;
119 VkPipeline graphicsPipeline;
120 VkCommandPool commandPool;
121
122 vector<VkFramebuffer> swapChainFramebuffers;
123 vector<VkCommandBuffer> commandBuffers;
124
125 vector<VkSemaphore> imageAvailableSemaphores;
126 vector<VkSemaphore> renderFinishedSemaphores;
127 vector<VkFence> inFlightFences;
128
129 size_t currentFrame = 0;
130
131 bool framebufferResized = false;
132
133 // both SDL and GLFW create window functions return NULL on failure
134 bool initWindow() {
135 if (gui->Init() == RTWO_ERROR) {
136 cout << "UI library could not be initialized!" << endl;
137 return RTWO_ERROR;
138 } else {
139 // On Apple's OS X you must set the NSHighResolutionCapable Info.plist property to YES,
140 // otherwise you will not receive a High DPI OpenGL canvas.
141
142 // TODO: Move this into some generic method in game-gui-sdl
143 window = SDL_CreateWindow("Vulkan Game",
144 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
145 SCREEN_WIDTH, SCREEN_HEIGHT,
146 SDL_WINDOW_VULKAN | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
147
148 if (window == nullptr) {
149 cout << "Window could not be created!" << endl;
150 return RTWO_ERROR;
151 } else {
152 return RTWO_SUCCESS;
153 }
154 }
155 }
156
157 void initVulkan() {
158 createInstance();
159 setupDebugMessenger();
160 createSurface();
161 pickPhysicalDevice();
162 createLogicalDevice();
163 createSwapChain();
164 createImageViews();
165 createRenderPass();
166 createGraphicsPipeline();
167 createFramebuffers();
168 createCommandPool();
169 createCommandBuffers();
170 createSyncObjects();
171 }
172
173 void recreateSwapChain() {
174 int width = 0, height = 0;
175 SDL_GetWindowSize(window, &width, &height);
176
177 while (width == 0 || height == 0 ||
178 (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) != 0) {
179 SDL_WaitEvent(nullptr);
180 SDL_GetWindowSize(window, &width, &height);
181 }
182
183 vkDeviceWaitIdle(device);
184
185 cleanupSwapChain();
186
187 createSwapChain();
188 createImageViews();
189 createRenderPass();
190 createGraphicsPipeline();
191 createFramebuffers();
192 createCommandBuffers();
193 }
194
195 void cleanupSwapChain() {
196 for (auto framebuffer : swapChainFramebuffers) {
197 vkDestroyFramebuffer(device, framebuffer, nullptr);
198 }
199
200 vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
201
202 vkDestroyPipeline(device, graphicsPipeline, nullptr);
203 vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
204 vkDestroyRenderPass(device, renderPass, nullptr);
205
206 for (auto imageView : swapChainImageViews) {
207 vkDestroyImageView(device, imageView, nullptr);
208 }
209
210 vkDestroySwapchainKHR(device, swapChain, nullptr);
211 }
212
213 void createInstance() {
214 if (enableValidationLayers && !checkValidationLayerSupport()) {
215 throw runtime_error("validation layers requested, but not available!");
216 }
217
218 VkApplicationInfo appInfo = {};
219 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
220 appInfo.pApplicationName = "Vulkan Game";
221 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
222 appInfo.pEngineName = "No Engine";
223 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
224 appInfo.apiVersion = VK_API_VERSION_1_0;
225
226 VkInstanceCreateInfo createInfo = {};
227 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
228 createInfo.pApplicationInfo = &appInfo;
229
230 vector<const char*> extensions = getRequiredExtensions();
231 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
232 createInfo.ppEnabledExtensionNames = extensions.data();
233
234 cout << endl << "SDL extensions:" << endl;
235 for (const char* extensionName : extensions) {
236 cout << extensionName << endl;
237 }
238 cout << endl;
239
240 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
241 if (enableValidationLayers) {
242 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
243 createInfo.ppEnabledLayerNames = validationLayers.data();
244
245 populateDebugMessengerCreateInfo(debugCreateInfo);
246 createInfo.pNext = &debugCreateInfo;
247 } else {
248 createInfo.enabledLayerCount = 0;
249
250 createInfo.pNext = nullptr;
251 }
252
253 if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
254 throw runtime_error("failed to create instance!");
255 }
256 }
257
258 void setupDebugMessenger() {
259 if (!enableValidationLayers) return;
260
261 VkDebugUtilsMessengerCreateInfoEXT createInfo;
262 populateDebugMessengerCreateInfo(createInfo);
263
264 if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
265 throw runtime_error("failed to setup debug messenger!");
266 }
267 }
268
269 void createSurface() {
270 sdlSurface = SDL_GetWindowSurface(window);
271
272 if (sdlSurface == nullptr) {
273 cout << "Could not get SDL Surface! =(" << endl;
274 }
275
276 if (!SDL_Vulkan_CreateSurface(window, instance, &surface)) {
277 throw runtime_error("failed to create window surface!");
278 }
279
280 /*
281 if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) {
282 throw runtime_error("failed to create window surface!");
283 }
284 */
285 }
286
287 void pickPhysicalDevice() {
288 uint32_t deviceCount = 0;
289 vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
290
291 if (deviceCount == 0) {
292 throw runtime_error("failed to find GPUs with Vulkan support!");
293 }
294
295 vector<VkPhysicalDevice> devices(deviceCount);
296 vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
297
298 cout << endl << "Graphics cards:" << endl;
299 for (const VkPhysicalDevice& device : devices) {
300 if (isDeviceSuitable(device)) {
301 physicalDevice = device;
302 break;
303 }
304 }
305 cout << endl;
306
307 if (physicalDevice == VK_NULL_HANDLE) {
308 throw runtime_error("failed to find a suitable GPU!");
309 }
310 }
311
312 bool isDeviceSuitable(VkPhysicalDevice device) {
313 VkPhysicalDeviceProperties deviceProperties;
314 VkPhysicalDeviceFeatures deviceFeatures;
315
316 vkGetPhysicalDeviceProperties(device, &deviceProperties);
317 vkGetPhysicalDeviceFeatures(device, &deviceFeatures);
318
319 cout << "Device: " << deviceProperties.deviceName << endl;
320
321 QueueFamilyIndices indices = findQueueFamilies(device);
322
323 bool extensionsSupported = checkDeviceExtensionSupport(device);
324
325 bool swapChainAdequate = false;
326
327 if (extensionsSupported) {
328 SwapChainSupportDetails swapChainSupport = querySwapChainSupport(device);
329 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
330 }
331
332 return indices.isComplete() && extensionsSupported && swapChainAdequate;
333 }
334
335 bool checkDeviceExtensionSupport(VkPhysicalDevice device) {
336 uint32_t extensionCount;
337 vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
338
339 vector<VkExtensionProperties> availableExtensions(extensionCount);
340 vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, availableExtensions.data());
341
342 set<string> requiredExtensions(deviceExtensions.begin(), deviceExtensions.end());
343
344 for (const auto& extension : availableExtensions) {
345 requiredExtensions.erase(extension.extensionName);
346 }
347
348 return requiredExtensions.empty();
349 }
350
351 void createLogicalDevice() {
352 QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
353
354 vector<VkDeviceQueueCreateInfo> queueCreateInfos;
355 set<uint32_t> uniqueQueueFamilies = {indices.graphicsFamily.value(), indices.presentFamily.value()};
356
357 float queuePriority = 1.0f;
358 for (uint32_t queueFamily : uniqueQueueFamilies) {
359 VkDeviceQueueCreateInfo queueCreateInfo = {};
360
361 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
362 queueCreateInfo.queueFamilyIndex = queueFamily;
363 queueCreateInfo.queueCount = 1;
364 queueCreateInfo.pQueuePriorities = &queuePriority;
365
366 queueCreateInfos.push_back(queueCreateInfo);
367 }
368
369 VkPhysicalDeviceFeatures deviceFeatures = {};
370
371 VkDeviceCreateInfo createInfo = {};
372 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
373
374 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());;
375 createInfo.pQueueCreateInfos = queueCreateInfos.data();
376
377 createInfo.pEnabledFeatures = &deviceFeatures;
378
379 createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
380 createInfo.ppEnabledExtensionNames = deviceExtensions.data();
381
382 // These fields are ignored by up-to-date Vulkan implementations,
383 // but it's a good idea to set them for backwards compatibility
384 if (enableValidationLayers) {
385 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
386 createInfo.ppEnabledLayerNames = validationLayers.data();
387 } else {
388 createInfo.enabledLayerCount = 0;
389 }
390
391 if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
392 throw runtime_error("failed to create logical device!");
393 }
394
395 vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
396 vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
397 }
398
399 bool checkValidationLayerSupport() {
400 uint32_t layerCount;
401 vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
402
403 vector<VkLayerProperties> availableLayers(layerCount);
404 vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
405
406 for (const char* layerName : validationLayers) {
407 bool layerFound = false;
408
409 for (const auto& layerProperties : availableLayers) {
410 if (strcmp(layerName, layerProperties.layerName) == 0) {
411 layerFound = true;
412 break;
413 }
414 }
415
416 if (!layerFound) {
417 return false;
418 }
419 }
420
421 return true;
422 }
423
424 QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
425 QueueFamilyIndices indices;
426
427 uint32_t queueFamilyCount = 0;
428 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
429
430 vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
431 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
432
433 int i = 0;
434 for (const auto& queueFamily : queueFamilies) {
435 if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
436 indices.graphicsFamily = i;
437 }
438
439 VkBool32 presentSupport = false;
440 vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
441
442 if (queueFamily.queueCount > 0 && presentSupport) {
443 indices.presentFamily = i;
444 }
445
446 if (indices.isComplete()) {
447 break;
448 }
449
450 i++;
451 }
452
453 return indices;
454 }
455
456 SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device) {
457 SwapChainSupportDetails details;
458
459 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities);
460
461 uint32_t formatCount;
462 vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, nullptr);
463
464 if (formatCount != 0) {
465 details.formats.resize(formatCount);
466 vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, details.formats.data());
467 }
468
469 uint32_t presentModeCount;
470 vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, nullptr);
471
472 if (presentModeCount != 0) {
473 details.presentModes.resize(presentModeCount);
474 vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, details.presentModes.data());
475 }
476
477 return details;
478 }
479
480 VkSurfaceFormatKHR chooseSwapSurfaceFormat(const vector<VkSurfaceFormatKHR>& availableFormats) {
481 for (const auto& availableFormat : availableFormats) {
482 if (availableFormat.format == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
483 return availableFormat;
484 }
485 }
486
487 return availableFormats[0];
488 }
489
490 VkPresentModeKHR chooseSwapPresentMode(const vector<VkPresentModeKHR>& availablePresentModes) {
491 VkPresentModeKHR bestMode = VK_PRESENT_MODE_FIFO_KHR;
492
493 for (const auto& availablePresentMode : availablePresentModes) {
494 if (availablePresentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
495 return availablePresentMode;
496 } else if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) {
497 bestMode = availablePresentMode;
498 }
499 }
500
501 return bestMode;
502 }
503
504 VkExtent2D chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities) {
505 if (capabilities.currentExtent.width != numeric_limits<uint32_t>::max()) {
506 return capabilities.currentExtent;
507 } else {
508 int width, height;
509 SDL_GetWindowSize(window, &width, &height);
510
511 VkExtent2D actualExtent = {
512 static_cast<uint32_t>(width),
513 static_cast<uint32_t>(height)
514 };
515
516 actualExtent.width = max(capabilities.minImageExtent.width, min(capabilities.maxImageExtent.width, actualExtent.width));
517 actualExtent.height = max(capabilities.minImageExtent.height, min(capabilities.maxImageExtent.height, actualExtent.height));
518
519 return actualExtent;
520 }
521 }
522
523 void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
524 createInfo = {};
525 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
526 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;
527 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;
528 createInfo.pfnUserCallback = debugCallback;
529 }
530
531 vector<const char*> getRequiredExtensions() {
532 uint32_t extensionCount = 0;
533 SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, nullptr);
534
535 vector<const char*> extensions(extensionCount);
536 SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, extensions.data());
537
538 if (enableValidationLayers) {
539 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
540 }
541
542 return extensions;
543 }
544
545 void createSwapChain() {
546 SwapChainSupportDetails swapChainSupport = querySwapChainSupport(physicalDevice);
547
548 VkSurfaceFormatKHR surfaceFormat = chooseSwapSurfaceFormat(swapChainSupport.formats);
549 VkPresentModeKHR presentMode = chooseSwapPresentMode(swapChainSupport.presentModes);
550 VkExtent2D extent = chooseSwapExtent(swapChainSupport.capabilities);
551
552 uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
553 if (swapChainSupport.capabilities.maxImageCount > 0 &&
554 imageCount > swapChainSupport.capabilities.maxImageCount) {
555 imageCount = swapChainSupport.capabilities.maxImageCount;
556 }
557
558 VkSwapchainCreateInfoKHR createInfo = {};
559
560 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
561 createInfo.surface = surface;
562 createInfo.minImageCount = imageCount;
563 createInfo.imageFormat = surfaceFormat.format;
564 createInfo.imageColorSpace = surfaceFormat.colorSpace;
565 createInfo.imageExtent = extent;
566 createInfo.imageArrayLayers = 1;
567 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
568
569 QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
570 uint32_t queueFamilyIndices[] = {indices.graphicsFamily.value(), indices.presentFamily.value()};
571
572 if (indices.graphicsFamily != indices.presentFamily) {
573 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
574 createInfo.queueFamilyIndexCount = 2;
575 createInfo.pQueueFamilyIndices = queueFamilyIndices;
576 } else {
577 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
578 createInfo.queueFamilyIndexCount = 0; // Optional
579 createInfo.pQueueFamilyIndices = nullptr;
580 }
581
582 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
583 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
584 createInfo.presentMode = presentMode;
585 createInfo.clipped = VK_TRUE;
586 createInfo.oldSwapchain = VK_NULL_HANDLE;
587
588 if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
589 throw runtime_error("failed to create swap chain!");
590 }
591
592 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
593 swapChainImages.resize(imageCount);
594 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
595
596 swapChainImageFormat = surfaceFormat.format;
597 swapChainExtent = extent;
598 }
599
600 void createImageViews() {
601 swapChainImageViews.resize(swapChainImages.size());
602
603 for (size_t i=0; i<swapChainImages.size(); i++) {
604 VkImageViewCreateInfo createInfo = {};
605 createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
606 createInfo.image = swapChainImages[i];
607
608 createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
609 createInfo.format = swapChainImageFormat;
610
611 createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
612 createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
613 createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
614 createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
615
616 createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
617 createInfo.subresourceRange.baseMipLevel = 0;
618 createInfo.subresourceRange.levelCount = 1;
619 createInfo.subresourceRange.baseArrayLayer = 0;
620 createInfo.subresourceRange.layerCount = 1;
621
622 if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) {
623 throw runtime_error("failed to create image views!");
624 }
625 }
626 }
627
628 void createRenderPass() {
629 VkAttachmentDescription colorAttachment = {};
630 colorAttachment.format = swapChainImageFormat;
631 colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
632 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
633 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
634 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
635 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
636 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
637 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
638
639 VkAttachmentReference colorAttachmentRef = {};
640 colorAttachmentRef.attachment = 0;
641 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
642
643 VkSubpassDescription subpass = {};
644 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
645 subpass.colorAttachmentCount = 1;
646 subpass.pColorAttachments = &colorAttachmentRef;
647
648 VkSubpassDependency dependency = {};
649 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
650 dependency.dstSubpass = 0;
651 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
652 dependency.srcAccessMask = 0;
653 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
654 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
655
656 VkRenderPassCreateInfo renderPassInfo = {};
657 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
658 renderPassInfo.attachmentCount = 1;
659 renderPassInfo.pAttachments = &colorAttachment;
660 renderPassInfo.subpassCount = 1;
661 renderPassInfo.pSubpasses = &subpass;
662 renderPassInfo.dependencyCount = 1;
663 renderPassInfo.pDependencies = &dependency;
664
665 if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
666 throw runtime_error("failed to create render pass!");
667 }
668 }
669
670 void createGraphicsPipeline() {
671 auto vertShaderCode = readFile("shaders/vert.spv");
672 auto fragShaderCode = readFile("shaders/frag.spv");
673
674 VkShaderModule vertShaderModule = createShaderModule(vertShaderCode);
675 VkShaderModule fragShaderModule = createShaderModule(fragShaderCode);
676
677 VkPipelineShaderStageCreateInfo vertShaderStageInfo = {};
678 vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
679 vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
680 vertShaderStageInfo.module = vertShaderModule;
681 vertShaderStageInfo.pName = "main";
682
683 VkPipelineShaderStageCreateInfo fragShaderStageInfo = {};
684 fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
685 fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
686 fragShaderStageInfo.module = fragShaderModule;
687 fragShaderStageInfo.pName = "main";
688
689 VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
690
691 VkPipelineVertexInputStateCreateInfo vertexInputInfo = {};
692 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
693 vertexInputInfo.vertexBindingDescriptionCount = 0;
694 vertexInputInfo.vertexAttributeDescriptionCount = 0;
695
696 VkPipelineInputAssemblyStateCreateInfo inputAssembly = {};
697 inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
698 inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
699 inputAssembly.primitiveRestartEnable = VK_FALSE;
700
701 VkViewport viewport = {};
702 viewport.x = 0.0f;
703 viewport.y = 0.0f;
704 viewport.width = (float) swapChainExtent.width;
705 viewport.height = (float) swapChainExtent.height;
706 viewport.minDepth = 0.0f;
707 viewport.maxDepth = 1.0f;
708
709 VkRect2D scissor = {};
710 scissor.offset = { 0, 0 };
711 scissor.extent = swapChainExtent;
712
713 VkPipelineViewportStateCreateInfo viewportState = {};
714 viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
715 viewportState.viewportCount = 1;
716 viewportState.pViewports = &viewport;
717 viewportState.scissorCount = 1;
718 viewportState.pScissors = &scissor;
719
720 VkPipelineRasterizationStateCreateInfo rasterizer = {};
721 rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
722 rasterizer.depthClampEnable = VK_FALSE;
723 rasterizer.rasterizerDiscardEnable = VK_FALSE;
724 rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
725 rasterizer.lineWidth = 1.0f;
726 rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
727 rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
728 rasterizer.depthBiasEnable = false;
729
730 VkPipelineMultisampleStateCreateInfo multisampling = {};
731 multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
732 multisampling.sampleShadingEnable = VK_FALSE;
733 multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
734
735 VkPipelineColorBlendAttachmentState colorBlendAttachment = {};
736 colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
737 colorBlendAttachment.blendEnable = VK_FALSE;
738
739 VkPipelineColorBlendStateCreateInfo colorBlending = {};
740 colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
741 colorBlending.logicOpEnable = VK_FALSE;
742 colorBlending.logicOp = VK_LOGIC_OP_COPY;
743 colorBlending.attachmentCount = 1;
744 colorBlending.pAttachments = &colorBlendAttachment;
745 colorBlending.blendConstants[0] = 0.0f;
746 colorBlending.blendConstants[1] = 0.0f;
747 colorBlending.blendConstants[2] = 0.0f;
748 colorBlending.blendConstants[3] = 0.0f;
749
750 VkPipelineLayoutCreateInfo pipelineLayoutInfo = {};
751 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
752 pipelineLayoutInfo.setLayoutCount = 0;
753 pipelineLayoutInfo.pushConstantRangeCount = 0;
754
755 if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
756 throw runtime_error("failed to create pipeline layout!");
757 }
758
759 VkGraphicsPipelineCreateInfo pipelineInfo = {};
760 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
761 pipelineInfo.stageCount = 2;
762 pipelineInfo.pStages = shaderStages;
763 pipelineInfo.pVertexInputState = &vertexInputInfo;
764 pipelineInfo.pInputAssemblyState = &inputAssembly;
765 pipelineInfo.pViewportState = &viewportState;
766 pipelineInfo.pRasterizationState = &rasterizer;
767 pipelineInfo.pMultisampleState = &multisampling;
768 pipelineInfo.pDepthStencilState = nullptr;
769 pipelineInfo.pColorBlendState = &colorBlending;
770 pipelineInfo.pDynamicState = nullptr;
771 pipelineInfo.layout = pipelineLayout;
772 pipelineInfo.renderPass = renderPass;
773 pipelineInfo.subpass = 0;
774 pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
775 pipelineInfo.basePipelineIndex = -1;
776
777 if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {
778 throw runtime_error("failed to create graphics pipeline!");
779 }
780
781 vkDestroyShaderModule(device, vertShaderModule, nullptr);
782 vkDestroyShaderModule(device, fragShaderModule, nullptr);
783 }
784
785 VkShaderModule createShaderModule(const vector<char>& code) {
786 VkShaderModuleCreateInfo createInfo = {};
787 createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
788 createInfo.codeSize = code.size();
789 createInfo.pCode = reinterpret_cast<const uint32_t*>(code.data());
790
791 VkShaderModule shaderModule;
792 if (vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) {
793 throw runtime_error("failed to create shader module!");
794 }
795
796 return shaderModule;
797 }
798
799 void createFramebuffers() {
800 swapChainFramebuffers.resize(swapChainImageViews.size());
801
802 for (size_t i = 0; i < swapChainImageViews.size(); i++) {
803 VkImageView attachments[] = {
804 swapChainImageViews[i]
805 };
806
807 VkFramebufferCreateInfo framebufferInfo = {};
808 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
809 framebufferInfo.renderPass = renderPass;
810 framebufferInfo.attachmentCount = 1;
811 framebufferInfo.pAttachments = attachments;
812 framebufferInfo.width = swapChainExtent.width;
813 framebufferInfo.height = swapChainExtent.height;
814 framebufferInfo.layers = 1;
815
816 if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
817 throw runtime_error("failed to create framebuffer!");
818 }
819 }
820 }
821
822 void createCommandPool() {
823 QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);
824
825 VkCommandPoolCreateInfo poolInfo = {};
826 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
827 poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
828 poolInfo.flags = 0;
829
830 if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
831 throw runtime_error("failed to create command pool!");
832 }
833 }
834
835 void createCommandBuffers() {
836 commandBuffers.resize(swapChainFramebuffers.size());
837
838 VkCommandBufferAllocateInfo allocInfo = {};
839 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
840 allocInfo.commandPool = commandPool;
841 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
842 allocInfo.commandBufferCount = (uint32_t)commandBuffers.size();
843
844 if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
845 throw runtime_error("failed to create command buffers!");
846 }
847
848 for (size_t i = 0; i < commandBuffers.size(); i++) {
849 VkCommandBufferBeginInfo beginInfo = {};
850 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
851 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
852 beginInfo.pInheritanceInfo = nullptr;
853
854 if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
855 throw runtime_error("failed to begin recording command buffer!");
856 }
857
858 VkRenderPassBeginInfo renderPassInfo = {};
859 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
860 renderPassInfo.renderPass = renderPass;
861 renderPassInfo.framebuffer = swapChainFramebuffers[i];
862 renderPassInfo.renderArea.offset = { 0, 0 };
863 renderPassInfo.renderArea.extent = swapChainExtent;
864
865 VkClearValue clearColor = { 0.0f, 0.0f, 0.0f, 1.0f };
866 renderPassInfo.clearValueCount = 1;
867 renderPassInfo.pClearValues = &clearColor;
868
869 vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
870 vkCmdBindPipeline(commandBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
871 vkCmdDraw(commandBuffers[i], 3, 1, 0, 0);
872 vkCmdEndRenderPass(commandBuffers[i]);
873
874 if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
875 throw runtime_error("failed to record command buffer!");
876 }
877 }
878 }
879
880 void createSyncObjects() {
881 imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
882 renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
883 inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
884
885 VkSemaphoreCreateInfo semaphoreInfo = {};
886 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
887
888 VkFenceCreateInfo fenceInfo = {};
889 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
890 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
891
892 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
893 if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS ||
894 vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS ||
895 vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {
896 throw runtime_error("failed to create synchronization objects for a frame!");
897 }
898 }
899 }
900
901 void mainLoop() {
902 // TODO: Create some generic event-handling functions in game-gui-*
903 SDL_Event e;
904 bool quit = false;
905
906 while (!quit) {
907 while (SDL_PollEvent(&e)) {
908 if (e.type == SDL_QUIT) {
909 quit = true;
910 }
911 if (e.type == SDL_KEYDOWN) {
912 quit = true;
913 }
914 if (e.type == SDL_MOUSEBUTTONDOWN) {
915 quit = true;
916 }
917 if (e.type == SDL_WINDOWEVENT) {
918 if (e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) {
919 framebufferResized = true;
920 } else if (e.window.event == SDL_WINDOWEVENT_MINIMIZED) {
921 framebufferResized = true;
922 }
923 }
924 }
925
926 drawFrame();
927
928 //SDL_FillRect(sdlSurface, nullptr, SDL_MapRGB(sdlSurface->format, 0x00, 0x99, 0x99));
929 //SDL_UpdateWindowSurface(window);
930 }
931
932 vkDeviceWaitIdle(device);
933 }
934
935 void drawFrame() {
936 vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, numeric_limits<uint64_t>::max());
937
938 uint32_t imageIndex;
939
940 VkResult result = vkAcquireNextImageKHR(device, swapChain, numeric_limits<uint64_t>::max(), imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
941
942 if (result == VK_ERROR_OUT_OF_DATE_KHR) {
943 recreateSwapChain();
944 return;
945 } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
946 throw runtime_error("failed to acquire swap chain image!");
947 }
948
949 VkSubmitInfo submitInfo = {};
950 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
951
952 VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] };
953 VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
954
955 submitInfo.waitSemaphoreCount = 1;
956 submitInfo.pWaitSemaphores = waitSemaphores;
957 submitInfo.pWaitDstStageMask = waitStages;
958 submitInfo.commandBufferCount = 1;
959 submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
960
961 VkSemaphore signalSemaphores[] = { renderFinishedSemaphores[currentFrame] };
962
963 submitInfo.signalSemaphoreCount = 1;
964 submitInfo.pSignalSemaphores = signalSemaphores;
965
966 vkResetFences(device, 1, &inFlightFences[currentFrame]);
967
968 if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
969 throw runtime_error("failed to submit draw command buffer!");
970 }
971
972 VkPresentInfoKHR presentInfo = {};
973 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
974
975 presentInfo.waitSemaphoreCount = 1;
976 presentInfo.pWaitSemaphores = signalSemaphores;
977
978 VkSwapchainKHR swapChains[] = { swapChain };
979 presentInfo.swapchainCount = 1;
980 presentInfo.pSwapchains = swapChains;
981 presentInfo.pImageIndices = &imageIndex;
982 presentInfo.pResults = nullptr;
983
984 result = vkQueuePresentKHR(presentQueue, &presentInfo);
985
986 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {
987 framebufferResized = false;
988 recreateSwapChain();
989 } else if (result != VK_SUCCESS) {
990 throw runtime_error("failed to present swap chain image!");
991 }
992
993 currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
994 }
995
996 void cleanup() {
997 cleanupSwapChain();
998
999 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
1000 vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
1001 vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
1002 vkDestroyFence(device, inFlightFences[i], nullptr);
1003 }
1004
1005 vkDestroyCommandPool(device, commandPool, nullptr);
1006
1007 vkDestroyDevice(device, nullptr);
1008
1009 if (enableValidationLayers) {
1010 DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
1011 }
1012
1013 vkDestroySurfaceKHR(instance, surface, nullptr);
1014 vkDestroyInstance(instance, nullptr);
1015
1016 // TODO: Move this into some generic method in game-gui-sdl
1017 SDL_DestroyWindow(window);
1018
1019 gui->Shutdown();
1020 delete gui;
1021 }
1022
1023 static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
1024 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
1025 VkDebugUtilsMessageTypeFlagsEXT messageType,
1026 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
1027 void* pUserData) {
1028 cerr << "validation layer: " << pCallbackData->pMessage << endl;
1029
1030 return VK_FALSE;
1031}
1032
1033 static vector<char> readFile(const string& filename) {
1034 ifstream file(filename, ios::ate | ios::binary);
1035
1036 if (!file.is_open()) {
1037 throw runtime_error("failed to open file!");
1038 }
1039
1040 size_t fileSize = (size_t)file.tellg();
1041 vector<char> buffer(fileSize);
1042
1043 file.seekg(0);
1044 file.read(buffer.data(), fileSize);
1045
1046 file.close();
1047
1048 return buffer;
1049 }
1050};
1051
1052int main(int argc, char* argv[]) {
1053
1054#ifdef NDEBUG
1055 cout << "DEBUGGING IS OFF" << endl;
1056#else
1057 cout << "DEBUGGING IS ON" << endl;
1058#endif
1059
1060 glm::mat4 matrix;
1061 glm::vec4 vec;
1062 glm::vec4 test = matrix * vec;
1063
1064 cout << "Starting Vulkan game..." << endl;
1065
1066 VulkanGame game;
1067
1068 try {
1069 game.run();
1070 } catch (const exception& e) {
1071 cerr << e.what() << endl;
1072 return EXIT_FAILURE;
1073 }
1074
1075 cout << "Finished running the game" << endl;
1076
1077 return EXIT_SUCCESS;
1078}
Note: See TracBrowser for help on using the repository browser.