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

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

Enable rendering of geometry using multiple sets of vertex and index buffers and create a BufferInfo object to store the Vulkan objects and data related to each set

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