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

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

Use an index buffer to avoid having duplicate points in the vertex buffer

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