source: opengl-game/vulkan-game.cpp@ 47bff4c

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

Create the commnand buffers and sync objects

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