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

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

Fix the SDL overlay in Windows 10

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