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

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

Add a depth buffer for depth testing

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