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

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

Create an initGraphicsPipelines() function and templatize and generalize the createUniformBuffers() functionso it can be used to create a uniform buffer of any type

  • Property mode set to 100644
File size: 38.6 KB
Line 
1#include "vulkan-game.hpp"
2
3#define GLM_FORCE_RADIANS
4#define GLM_FORCE_DEPTH_ZERO_TO_ONE
5
6#include <array>
7#include <chrono>
8#include <iostream>
9#include <set>
10
11#include "consts.hpp"
12#include "logger.hpp"
13
14#include "utils.hpp"
15
16using namespace std;
17
18VulkanGame::VulkanGame(int maxFramesInFlight) : MAX_FRAMES_IN_FLIGHT(maxFramesInFlight) {
19 gui = nullptr;
20 window = nullptr;
21 font = nullptr;
22 fontSDLTexture = nullptr;
23 imageSDLTexture = nullptr;
24
25 currentFrame = 0;
26 framebufferResized = false;
27
28 modelMvpMats = {};
29}
30
31VulkanGame::~VulkanGame() {
32}
33
34void VulkanGame::run(int width, int height, unsigned char guiFlags) {
35 cout << "DEBUGGING IS " << (ENABLE_VALIDATION_LAYERS ? "ON" : "OFF") << endl;
36
37 cout << "Vulkan Game" << endl;
38
39 // This gets the runtime version, use SDL_VERSION() for the comppile-time version
40 // TODO: Create a game-gui function to get the gui version and retrieve it that way
41 SDL_GetVersion(&sdlVersion);
42
43 // TODO: Refactor the logger api to be more flexible,
44 // esp. since gl_log() and gl_log_err() have issues printing anything besides stirngs
45 restart_gl_log();
46 gl_log("starting SDL\n%s.%s.%s",
47 to_string(sdlVersion.major).c_str(),
48 to_string(sdlVersion.minor).c_str(),
49 to_string(sdlVersion.patch).c_str());
50
51 open_log();
52 get_log() << "starting SDL" << endl;
53 get_log() <<
54 (int)sdlVersion.major << "." <<
55 (int)sdlVersion.minor << "." <<
56 (int)sdlVersion.patch << endl;
57
58 if (initWindow(width, height, guiFlags) == RTWO_ERROR) {
59 return;
60 }
61
62 initVulkan();
63 initMatrices();
64 mainLoop();
65 cleanup();
66
67 close_log();
68}
69
70// TODO: Make some more init functions, or call this initUI if the
71// amount of things initialized here keeps growing
72bool VulkanGame::initWindow(int width, int height, unsigned char guiFlags) {
73 // TODO: Put all fonts, textures, and images in the assets folder
74 gui = new GameGui_SDL();
75
76 if (gui->init() == RTWO_ERROR) {
77 // TODO: Also print these sorts of errors to the log
78 cout << "UI library could not be initialized!" << endl;
79 cout << gui->getError() << endl;
80 return RTWO_ERROR;
81 }
82
83 window = (SDL_Window*) gui->createWindow("Vulkan Game", width, height, guiFlags & GUI_FLAGS_WINDOW_FULLSCREEN);
84 if (window == nullptr) {
85 cout << "Window could not be created!" << endl;
86 cout << gui->getError() << endl;
87 return RTWO_ERROR;
88 }
89
90 cout << "Target window size: (" << width << ", " << height << ")" << endl;
91 cout << "Actual window size: (" << gui->getWindowWidth() << ", " << gui->getWindowHeight() << ")" << endl;
92
93 renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
94 if (renderer == nullptr) {
95 cout << "Renderer could not be created!" << endl;
96 cout << gui->getError() << endl;
97 return RTWO_ERROR;
98 }
99
100 SDL_VERSION(&sdlVersion);
101
102 cout << "SDL " << sdlVersion.major << "." << sdlVersion.minor << "." << sdlVersion.patch << endl;
103
104 font = TTF_OpenFont("assets/fonts/lazy.ttf", 28);
105 if (font == nullptr) {
106 cout << "Failed to load lazy font! SDL_ttf Error: " << TTF_GetError() << endl;
107 return RTWO_ERROR;
108 }
109
110 SDL_Surface* fontSDLSurface = TTF_RenderText_Solid(font, "Great success!", { 255, 255, 255 });
111 if (fontSDLSurface == nullptr) {
112 cout << "Unable to render text surface! SDL_ttf Error: " << TTF_GetError() << endl;
113 return RTWO_ERROR;
114 }
115
116 fontSDLTexture = SDL_CreateTextureFromSurface(renderer, fontSDLSurface);
117 if (fontSDLTexture == nullptr) {
118 cout << "Unable to create texture from rendered text! SDL Error: " << SDL_GetError() << endl;
119 SDL_FreeSurface(fontSDLSurface);
120 return RTWO_ERROR;
121 }
122
123 SDL_FreeSurface(fontSDLSurface);
124
125 // TODO: Load a PNG instead
126 SDL_Surface* imageSDLSurface = SDL_LoadBMP("assets/images/spaceship.bmp");
127 if (imageSDLSurface == nullptr) {
128 cout << "Unable to load image " << "spaceship.bmp" << "! SDL Error: " << SDL_GetError() << endl;
129 return RTWO_ERROR;
130 }
131
132 imageSDLTexture = SDL_CreateTextureFromSurface(renderer, imageSDLSurface);
133 if (imageSDLTexture == nullptr) {
134 cout << "Unable to create texture from BMP surface! SDL Error: " << SDL_GetError() << endl;
135 SDL_FreeSurface(imageSDLSurface);
136 return RTWO_ERROR;
137 }
138
139 SDL_FreeSurface(imageSDLSurface);
140
141 // In SDL 2.0.10 (currently, the latest), SDL_TEXTUREACCESS_TARGET is required to get a transparent overlay working
142 // However, the latest SDL version available through homebrew on Mac is 2.0.9, which requires SDL_TEXTUREACCESS_STREAMING
143 // I tried building sdl 2.0.10 (and sdl_image and sdl_ttf) from source on Mac, but had some issues, so this is easier
144 // until the homebrew recipe is updated
145 if (sdlVersion.major == 2 && sdlVersion.minor == 0 && sdlVersion.patch == 9) {
146 uiOverlay = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING,
147 gui->getWindowWidth(), gui->getWindowHeight());
148 } else {
149 uiOverlay = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
150 gui->getWindowWidth(), gui->getWindowHeight());
151 }
152
153 if (uiOverlay == nullptr) {
154 cout << "Unable to create blank texture! SDL Error: " << SDL_GetError() << endl;
155 return RTWO_ERROR;
156 }
157 if (SDL_SetTextureBlendMode(uiOverlay, SDL_BLENDMODE_BLEND) != 0) {
158 cout << "Unable to set texture blend mode! SDL Error: " << SDL_GetError() << endl;
159 return RTWO_ERROR;
160 }
161
162 SDL_SetRenderTarget(renderer, uiOverlay);
163
164 return RTWO_SUCCESS;
165}
166
167void VulkanGame::initVulkan() {
168 const vector<const char*> validationLayers = {
169 "VK_LAYER_KHRONOS_validation"
170 };
171 const vector<const char*> deviceExtensions = {
172 VK_KHR_SWAPCHAIN_EXTENSION_NAME
173 };
174
175 createVulkanInstance(validationLayers);
176 setupDebugMessenger();
177 createVulkanSurface();
178 pickPhysicalDevice(deviceExtensions);
179 createLogicalDevice(validationLayers, deviceExtensions);
180 createSwapChain();
181 createImageViews();
182 createRenderPass();
183 createCommandPool();
184
185 createImageResources();
186 createFramebuffers();
187
188 // TODO: Figure out how much of ubo creation and associated variables should be in the pipeline class
189 // Maybe combine the ubo-related objects into a new class
190
191 initGraphicsPipelines();
192
193 modelPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ModelVertex::pos));
194 modelPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ModelVertex::color));
195 modelPipeline.addAttribute(VK_FORMAT_R32G32_SFLOAT, offset_of(&ModelVertex::texCoord));
196
197 createUniformBuffers<UBO_MvpMat>(uniformBuffers, uniformBuffersMemory, uniformBufferInfoList);
198
199 modelPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
200 VK_SHADER_STAGE_VERTEX_BIT, &uniformBufferInfoList);
201 modelPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
202 VK_SHADER_STAGE_FRAGMENT_BIT, &floorTextureImageDescriptor);
203
204 modelPipeline.addObject({
205 {{-0.5f, -0.5f, -2.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
206 {{ 0.5f, -0.5f, -2.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
207 {{ 0.5f, 0.5f, -2.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
208 {{-0.5f, 0.5f, -2.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
209 }, {
210 0, 1, 2, 2, 3, 0
211 }, commandPool, graphicsQueue);
212
213 modelPipeline.addObject({
214 {{-0.5f, -0.5f, -1.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
215 {{ 0.5f, -0.5f, -1.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
216 {{ 0.5f, 0.5f, -1.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
217 {{-0.5f, 0.5f, -1.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
218 }, {
219 0, 1, 2, 2, 3, 0
220 }, commandPool, graphicsQueue);
221
222 modelPipeline.createDescriptorSetLayout();
223 modelPipeline.createPipeline("shaders/scene-vert.spv", "shaders/scene-frag.spv");
224 modelPipeline.createDescriptorPool(swapChainImages);
225 modelPipeline.createDescriptorSets(swapChainImages);
226
227 overlayPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&OverlayVertex::pos));
228 overlayPipeline.addAttribute(VK_FORMAT_R32G32_SFLOAT, offset_of(&OverlayVertex::texCoord));
229
230 overlayPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
231 VK_SHADER_STAGE_FRAGMENT_BIT, &sdlOverlayImageDescriptor);
232
233 overlayPipeline.addObject({
234 {{-1.0f, 1.0f, 0.0f}, {0.0f, 1.0f}},
235 {{ 1.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
236 {{ 1.0f, -1.0f, 0.0f}, {1.0f, 0.0f}},
237 {{-1.0f, -1.0f, 0.0f}, {0.0f, 0.0f}}
238 }, {
239 0, 1, 2, 2, 3, 0
240 }, commandPool, graphicsQueue);
241
242 overlayPipeline.createDescriptorSetLayout();
243 overlayPipeline.createPipeline("shaders/overlay-vert.spv", "shaders/overlay-frag.spv");
244 overlayPipeline.createDescriptorPool(swapChainImages);
245 overlayPipeline.createDescriptorSets(swapChainImages);
246
247 cout << "Created all the graphics pipelines" << endl;
248
249 createCommandBuffers();
250
251 createSyncObjects();
252}
253
254void VulkanGame::initGraphicsPipelines() {
255 modelPipeline = GraphicsPipeline_Vulkan<ModelVertex>(physicalDevice, device, renderPass,
256 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, 16, 24);
257
258 overlayPipeline = GraphicsPipeline_Vulkan<OverlayVertex>(physicalDevice, device, renderPass,
259 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, 4, 6);
260}
261
262// TODO: Maybe changes the name to initScene() or something similar
263void VulkanGame::initMatrices() {
264 cam_pos = vec3(0.0f, 0.0f, 2.0f);
265
266 float cam_yaw = 0.0f;
267 float cam_pitch = -50.0f;
268
269 mat4 yaw_mat = rotate(mat4(1.0f), radians(-cam_yaw), vec3(0.0f, 1.0f, 0.0f));
270 mat4 pitch_mat = rotate(mat4(1.0f), radians(-cam_pitch), vec3(1.0f, 0.0f, 0.0f));
271
272 mat4 R_view = pitch_mat * yaw_mat;
273 mat4 T_view = translate(mat4(1.0f), vec3(-cam_pos.x, -cam_pos.y, -cam_pos.z));
274 mat4 view = R_view * T_view;
275
276 mat4 proj = perspective(radians(FOV_ANGLE), (float)swapChainExtent.width / (float)swapChainExtent.height, NEAR_CLIP, FAR_CLIP);
277 proj[1][1] *= -1; // flip the y-axis so that +y is up
278
279 modelMvpMats.model = mat4(1.0f);
280 modelMvpMats.view = view;
281 modelMvpMats.proj = proj;
282}
283
284void VulkanGame::mainLoop() {
285 UIEvent e;
286 bool quit = false;
287
288 while (!quit) {
289 gui->processEvents();
290
291 while (gui->pollEvent(&e)) {
292 switch(e.type) {
293 case UI_EVENT_QUIT:
294 cout << "Quit event detected" << endl;
295 quit = true;
296 break;
297 case UI_EVENT_WINDOW:
298 cout << "Window event detected" << endl;
299 // Currently unused
300 break;
301 case UI_EVENT_WINDOWRESIZE:
302 cout << "Window resize event detected" << endl;
303 framebufferResized = true;
304 break;
305 case UI_EVENT_KEYDOWN:
306 if (e.key.keycode == SDL_SCANCODE_ESCAPE) {
307 quit = true;
308 } else if (e.key.keycode == SDL_SCANCODE_SPACE) {
309 cout << "Adding a plane" << endl;
310 float zOffset = -2.0f + (0.5f * modelPipeline.getObjects().size());
311
312 vkDeviceWaitIdle(device);
313 vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
314
315 modelPipeline.addObject({
316 {{-0.5f, -0.5f, zOffset}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
317 {{ 0.5f, -0.5f, zOffset}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
318 {{ 0.5f, 0.5f, zOffset}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
319 {{-0.5f, 0.5f, zOffset}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
320 }, {
321 0, 1, 2, 2, 3, 0
322 }, commandPool, graphicsQueue);
323
324 createCommandBuffers();
325 } else {
326 cout << "Key event detected" << endl;
327 }
328 break;
329 case UI_EVENT_KEYUP:
330 break;
331 case UI_EVENT_MOUSEBUTTONDOWN:
332 cout << "Mouse button down event detected" << endl;
333 break;
334 case UI_EVENT_MOUSEBUTTONUP:
335 cout << "Mouse button up event detected" << endl;
336 break;
337 case UI_EVENT_MOUSEMOTION:
338 break;
339 case UI_EVENT_UNKNOWN:
340 cout << "Unknown event type: 0x" << hex << e.unknown.eventType << dec << endl;
341 break;
342 default:
343 cout << "Unhandled UI event: " << e.type << endl;
344 }
345 }
346
347 renderUI();
348 renderScene();
349 }
350
351 vkDeviceWaitIdle(device);
352}
353
354void VulkanGame::updateScene(uint32_t currentImage) {
355 static auto startTime = chrono::high_resolution_clock::now();
356
357 auto currentTime = chrono::high_resolution_clock::now();
358 float time = chrono::duration<float, chrono::seconds::period>(currentTime - startTime).count();
359
360 modelMvpMats.model =
361 translate(mat4(1.0f), vec3(0.0f, -2.0f, -0.0f)) *
362 rotate(mat4(1.0f), time * radians(90.0f), vec3(0.0f, 0.0f, 1.0f));
363
364 VulkanUtils::copyDataToMemory(device, uniformBuffersMemory[currentImage], modelMvpMats);
365}
366
367void VulkanGame::renderUI() {
368 SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
369 SDL_RenderClear(renderer);
370
371 SDL_Rect rect = {280, 220, 100, 100};
372 SDL_SetRenderDrawColor(renderer, 0x00, 0xFF, 0x00, 0xFF);
373 SDL_RenderFillRect(renderer, &rect);
374
375 rect = {10, 10, 0, 0};
376 SDL_QueryTexture(fontSDLTexture, nullptr, nullptr, &(rect.w), &(rect.h));
377 SDL_RenderCopy(renderer, fontSDLTexture, nullptr, &rect);
378
379 rect = {10, 80, 0, 0};
380 SDL_QueryTexture(imageSDLTexture, nullptr, nullptr, &(rect.w), &(rect.h));
381 SDL_RenderCopy(renderer, imageSDLTexture, nullptr, &rect);
382
383 SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0xFF, 0xFF);
384 SDL_RenderDrawLine(renderer, 50, 5, 150, 500);
385
386 VulkanUtils::populateVulkanImageFromSDLTexture(device, physicalDevice, commandPool, uiOverlay, renderer,
387 sdlOverlayImage, graphicsQueue);
388}
389
390void VulkanGame::renderScene() {
391 vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, numeric_limits<uint64_t>::max());
392
393 uint32_t imageIndex;
394
395 VkResult result = vkAcquireNextImageKHR(device, swapChain, numeric_limits<uint64_t>::max(),
396 imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
397
398 if (result == VK_ERROR_OUT_OF_DATE_KHR) {
399 recreateSwapChain();
400 return;
401 } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
402 throw runtime_error("failed to acquire swap chain image!");
403 }
404
405 // TODO: Figure out a more elegant way to only do updates and render the UI once per scene render
406 // Probably move some of the renderScene() code into a higher function that updates the UI, and renders
407 // the UI and scene
408 updateScene(imageIndex);
409
410 VkSubmitInfo submitInfo = {};
411 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
412
413 VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] };
414 VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
415
416 submitInfo.waitSemaphoreCount = 1;
417 submitInfo.pWaitSemaphores = waitSemaphores;
418 submitInfo.pWaitDstStageMask = waitStages;
419 submitInfo.commandBufferCount = 1;
420 submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
421
422 VkSemaphore signalSemaphores[] = { renderFinishedSemaphores[currentFrame] };
423
424 submitInfo.signalSemaphoreCount = 1;
425 submitInfo.pSignalSemaphores = signalSemaphores;
426
427 vkResetFences(device, 1, &inFlightFences[currentFrame]);
428
429 if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
430 throw runtime_error("failed to submit draw command buffer!");
431 }
432
433 VkPresentInfoKHR presentInfo = {};
434 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
435 presentInfo.waitSemaphoreCount = 1;
436 presentInfo.pWaitSemaphores = signalSemaphores;
437
438 VkSwapchainKHR swapChains[] = { swapChain };
439 presentInfo.swapchainCount = 1;
440 presentInfo.pSwapchains = swapChains;
441 presentInfo.pImageIndices = &imageIndex;
442 presentInfo.pResults = nullptr;
443
444 result = vkQueuePresentKHR(presentQueue, &presentInfo);
445
446 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {
447 framebufferResized = false;
448 recreateSwapChain();
449 } else if (result != VK_SUCCESS) {
450 throw runtime_error("failed to present swap chain image!");
451 }
452
453 currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
454 currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
455}
456
457void VulkanGame::cleanup() {
458 cleanupSwapChain();
459
460 VulkanUtils::destroyVulkanImage(device, floorTextureImage);
461 VulkanUtils::destroyVulkanImage(device, sdlOverlayImage);
462
463 vkDestroySampler(device, textureSampler, nullptr);
464
465 modelPipeline.cleanupBuffers();
466 overlayPipeline.cleanupBuffers();
467
468 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
469 vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
470 vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
471 vkDestroyFence(device, inFlightFences[i], nullptr);
472 }
473
474 vkDestroyCommandPool(device, commandPool, nullptr);
475 vkDestroyDevice(device, nullptr);
476 vkDestroySurfaceKHR(instance, surface, nullptr);
477
478 if (ENABLE_VALIDATION_LAYERS) {
479 VulkanUtils::destroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
480 }
481
482 vkDestroyInstance(instance, nullptr);
483
484 // TODO: Check if any of these functions accept null parameters
485 // If they do, I don't need to check for that
486
487 if (uiOverlay != nullptr) {
488 SDL_DestroyTexture(uiOverlay);
489 uiOverlay = nullptr;
490 }
491
492 if (fontSDLTexture != nullptr) {
493 SDL_DestroyTexture(fontSDLTexture);
494 fontSDLTexture = nullptr;
495 }
496
497 if (imageSDLTexture != nullptr) {
498 SDL_DestroyTexture(imageSDLTexture);
499 imageSDLTexture = nullptr;
500 }
501
502 TTF_CloseFont(font);
503 font = nullptr;
504
505 SDL_DestroyRenderer(renderer);
506 renderer = nullptr;
507
508 gui->destroyWindow();
509 gui->shutdown();
510 delete gui;
511}
512
513void VulkanGame::createVulkanInstance(const vector<const char*> &validationLayers) {
514 if (ENABLE_VALIDATION_LAYERS && !VulkanUtils::checkValidationLayerSupport(validationLayers)) {
515 throw runtime_error("validation layers requested, but not available!");
516 }
517
518 VkApplicationInfo appInfo = {};
519 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
520 appInfo.pApplicationName = "Vulkan Game";
521 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
522 appInfo.pEngineName = "No Engine";
523 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
524 appInfo.apiVersion = VK_API_VERSION_1_0;
525
526 VkInstanceCreateInfo createInfo = {};
527 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
528 createInfo.pApplicationInfo = &appInfo;
529
530 vector<const char*> extensions = gui->getRequiredExtensions();
531 if (ENABLE_VALIDATION_LAYERS) {
532 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
533 }
534
535 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
536 createInfo.ppEnabledExtensionNames = extensions.data();
537
538 cout << endl << "Extensions:" << endl;
539 for (const char* extensionName : extensions) {
540 cout << extensionName << endl;
541 }
542 cout << endl;
543
544 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
545 if (ENABLE_VALIDATION_LAYERS) {
546 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
547 createInfo.ppEnabledLayerNames = validationLayers.data();
548
549 populateDebugMessengerCreateInfo(debugCreateInfo);
550 createInfo.pNext = &debugCreateInfo;
551 } else {
552 createInfo.enabledLayerCount = 0;
553
554 createInfo.pNext = nullptr;
555 }
556
557 if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
558 throw runtime_error("failed to create instance!");
559 }
560}
561
562void VulkanGame::setupDebugMessenger() {
563 if (!ENABLE_VALIDATION_LAYERS) return;
564
565 VkDebugUtilsMessengerCreateInfoEXT createInfo;
566 populateDebugMessengerCreateInfo(createInfo);
567
568 if (VulkanUtils::createDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
569 throw runtime_error("failed to set up debug messenger!");
570 }
571}
572
573void VulkanGame::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
574 createInfo = {};
575 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
576 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;
577 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;
578 createInfo.pfnUserCallback = debugCallback;
579}
580
581VKAPI_ATTR VkBool32 VKAPI_CALL VulkanGame::debugCallback(
582 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
583 VkDebugUtilsMessageTypeFlagsEXT messageType,
584 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
585 void* pUserData) {
586 cerr << "validation layer: " << pCallbackData->pMessage << endl;
587
588 return VK_FALSE;
589}
590
591void VulkanGame::createVulkanSurface() {
592 if (gui->createVulkanSurface(instance, &surface) == RTWO_ERROR) {
593 throw runtime_error("failed to create window surface!");
594 }
595}
596
597void VulkanGame::pickPhysicalDevice(const vector<const char*>& deviceExtensions) {
598 uint32_t deviceCount = 0;
599 vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
600
601 if (deviceCount == 0) {
602 throw runtime_error("failed to find GPUs with Vulkan support!");
603 }
604
605 vector<VkPhysicalDevice> devices(deviceCount);
606 vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
607
608 cout << endl << "Graphics cards:" << endl;
609 for (const VkPhysicalDevice& device : devices) {
610 if (isDeviceSuitable(device, deviceExtensions)) {
611 physicalDevice = device;
612 break;
613 }
614 }
615 cout << endl;
616
617 if (physicalDevice == VK_NULL_HANDLE) {
618 throw runtime_error("failed to find a suitable GPU!");
619 }
620}
621
622bool VulkanGame::isDeviceSuitable(VkPhysicalDevice physicalDevice,
623 const vector<const char*>& deviceExtensions) {
624 VkPhysicalDeviceProperties deviceProperties;
625 vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
626
627 cout << "Device: " << deviceProperties.deviceName << endl;
628
629 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
630 bool extensionsSupported = VulkanUtils::checkDeviceExtensionSupport(physicalDevice, deviceExtensions);
631 bool swapChainAdequate = false;
632
633 if (extensionsSupported) {
634 SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
635 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
636 }
637
638 VkPhysicalDeviceFeatures supportedFeatures;
639 vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures);
640
641 return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
642}
643
644void VulkanGame::createLogicalDevice(
645 const vector<const char*> validationLayers,
646 const vector<const char*>& deviceExtensions) {
647 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
648
649 vector<VkDeviceQueueCreateInfo> queueCreateInfoList;
650 set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
651
652 float queuePriority = 1.0f;
653 for (uint32_t queueFamily : uniqueQueueFamilies) {
654 VkDeviceQueueCreateInfo queueCreateInfo = {};
655 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
656 queueCreateInfo.queueFamilyIndex = queueFamily;
657 queueCreateInfo.queueCount = 1;
658 queueCreateInfo.pQueuePriorities = &queuePriority;
659
660 queueCreateInfoList.push_back(queueCreateInfo);
661 }
662
663 VkPhysicalDeviceFeatures deviceFeatures = {};
664 deviceFeatures.samplerAnisotropy = VK_TRUE;
665
666 VkDeviceCreateInfo createInfo = {};
667 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
668 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfoList.size());
669 createInfo.pQueueCreateInfos = queueCreateInfoList.data();
670
671 createInfo.pEnabledFeatures = &deviceFeatures;
672
673 createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
674 createInfo.ppEnabledExtensionNames = deviceExtensions.data();
675
676 // These fields are ignored by up-to-date Vulkan implementations,
677 // but it's a good idea to set them for backwards compatibility
678 if (ENABLE_VALIDATION_LAYERS) {
679 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
680 createInfo.ppEnabledLayerNames = validationLayers.data();
681 } else {
682 createInfo.enabledLayerCount = 0;
683 }
684
685 if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
686 throw runtime_error("failed to create logical device!");
687 }
688
689 vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
690 vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
691}
692
693void VulkanGame::createSwapChain() {
694 SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
695
696 VkSurfaceFormatKHR surfaceFormat = VulkanUtils::chooseSwapSurfaceFormat(swapChainSupport.formats);
697 VkPresentModeKHR presentMode = VulkanUtils::chooseSwapPresentMode(swapChainSupport.presentModes);
698 VkExtent2D extent = VulkanUtils::chooseSwapExtent(swapChainSupport.capabilities, gui->getWindowWidth(), gui->getWindowHeight());
699
700 uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
701 if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) {
702 imageCount = swapChainSupport.capabilities.maxImageCount;
703 }
704
705 VkSwapchainCreateInfoKHR createInfo = {};
706 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
707 createInfo.surface = surface;
708 createInfo.minImageCount = imageCount;
709 createInfo.imageFormat = surfaceFormat.format;
710 createInfo.imageColorSpace = surfaceFormat.colorSpace;
711 createInfo.imageExtent = extent;
712 createInfo.imageArrayLayers = 1;
713 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
714
715 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
716 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
717
718 if (indices.graphicsFamily != indices.presentFamily) {
719 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
720 createInfo.queueFamilyIndexCount = 2;
721 createInfo.pQueueFamilyIndices = queueFamilyIndices;
722 } else {
723 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
724 createInfo.queueFamilyIndexCount = 0;
725 createInfo.pQueueFamilyIndices = nullptr;
726 }
727
728 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
729 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
730 createInfo.presentMode = presentMode;
731 createInfo.clipped = VK_TRUE;
732 createInfo.oldSwapchain = VK_NULL_HANDLE;
733
734 if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
735 throw runtime_error("failed to create swap chain!");
736 }
737
738 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
739 swapChainImages.resize(imageCount);
740 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
741
742 swapChainImageFormat = surfaceFormat.format;
743 swapChainExtent = extent;
744}
745
746void VulkanGame::createImageViews() {
747 swapChainImageViews.resize(swapChainImages.size());
748
749 for (size_t i = 0; i < swapChainImages.size(); i++) {
750 swapChainImageViews[i] = VulkanUtils::createImageView(device, swapChainImages[i], swapChainImageFormat,
751 VK_IMAGE_ASPECT_COLOR_BIT);
752 }
753}
754
755void VulkanGame::createRenderPass() {
756 VkAttachmentDescription colorAttachment = {};
757 colorAttachment.format = swapChainImageFormat;
758 colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
759 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
760 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
761 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
762 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
763 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
764 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
765
766 VkAttachmentReference colorAttachmentRef = {};
767 colorAttachmentRef.attachment = 0;
768 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
769
770 VkAttachmentDescription depthAttachment = {};
771 depthAttachment.format = findDepthFormat();
772 depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
773 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
774 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
775 depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
776 depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
777 depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
778 depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
779
780 VkAttachmentReference depthAttachmentRef = {};
781 depthAttachmentRef.attachment = 1;
782 depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
783
784 VkSubpassDescription subpass = {};
785 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
786 subpass.colorAttachmentCount = 1;
787 subpass.pColorAttachments = &colorAttachmentRef;
788 subpass.pDepthStencilAttachment = &depthAttachmentRef;
789
790 VkSubpassDependency dependency = {};
791 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
792 dependency.dstSubpass = 0;
793 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
794 dependency.srcAccessMask = 0;
795 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
796 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
797
798 array<VkAttachmentDescription, 2> attachments = { colorAttachment, depthAttachment };
799 VkRenderPassCreateInfo renderPassInfo = {};
800 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
801 renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
802 renderPassInfo.pAttachments = attachments.data();
803 renderPassInfo.subpassCount = 1;
804 renderPassInfo.pSubpasses = &subpass;
805 renderPassInfo.dependencyCount = 1;
806 renderPassInfo.pDependencies = &dependency;
807
808 if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
809 throw runtime_error("failed to create render pass!");
810 }
811}
812
813VkFormat VulkanGame::findDepthFormat() {
814 return VulkanUtils::findSupportedFormat(
815 physicalDevice,
816 { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
817 VK_IMAGE_TILING_OPTIMAL,
818 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
819 );
820}
821
822void VulkanGame::createCommandPool() {
823 QueueFamilyIndices queueFamilyIndices = VulkanUtils::findQueueFamilies(physicalDevice, surface);;
824
825 VkCommandPoolCreateInfo poolInfo = {};
826 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
827 poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
828 poolInfo.flags = 0;
829
830 if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
831 throw runtime_error("failed to create graphics command pool!");
832 }
833}
834
835void VulkanGame::createImageResources() {
836 VulkanUtils::createDepthImage(device, physicalDevice, commandPool, findDepthFormat(), swapChainExtent,
837 depthImage, graphicsQueue);
838
839 createTextureSampler();
840
841 VulkanUtils::createVulkanImageFromFile(device, physicalDevice, commandPool, "textures/texture.jpg",
842 floorTextureImage, graphicsQueue);
843
844 floorTextureImageDescriptor = {};
845 floorTextureImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
846 floorTextureImageDescriptor.imageView = floorTextureImage.imageView;
847 floorTextureImageDescriptor.sampler = textureSampler;
848
849 VulkanUtils::createVulkanImageFromSDLTexture(device, physicalDevice, uiOverlay, sdlOverlayImage);
850
851 sdlOverlayImageDescriptor = {};
852 sdlOverlayImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
853 sdlOverlayImageDescriptor.imageView = sdlOverlayImage.imageView;
854 sdlOverlayImageDescriptor.sampler = textureSampler;
855}
856
857void VulkanGame::createTextureSampler() {
858 VkSamplerCreateInfo samplerInfo = {};
859 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
860 samplerInfo.magFilter = VK_FILTER_LINEAR;
861 samplerInfo.minFilter = VK_FILTER_LINEAR;
862
863 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
864 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
865 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
866
867 samplerInfo.anisotropyEnable = VK_TRUE;
868 samplerInfo.maxAnisotropy = 16;
869 samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
870 samplerInfo.unnormalizedCoordinates = VK_FALSE;
871 samplerInfo.compareEnable = VK_FALSE;
872 samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
873 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
874 samplerInfo.mipLodBias = 0.0f;
875 samplerInfo.minLod = 0.0f;
876 samplerInfo.maxLod = 0.0f;
877
878 if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
879 throw runtime_error("failed to create texture sampler!");
880 }
881}
882
883void VulkanGame::createFramebuffers() {
884 swapChainFramebuffers.resize(swapChainImageViews.size());
885
886 for (size_t i = 0; i < swapChainImageViews.size(); i++) {
887 array<VkImageView, 2> attachments = {
888 swapChainImageViews[i],
889 depthImage.imageView
890 };
891
892 VkFramebufferCreateInfo framebufferInfo = {};
893 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
894 framebufferInfo.renderPass = renderPass;
895 framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
896 framebufferInfo.pAttachments = attachments.data();
897 framebufferInfo.width = swapChainExtent.width;
898 framebufferInfo.height = swapChainExtent.height;
899 framebufferInfo.layers = 1;
900
901 if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
902 throw runtime_error("failed to create framebuffer!");
903 }
904 }
905}
906
907void VulkanGame::createCommandBuffers() {
908 commandBuffers.resize(swapChainImages.size());
909
910 VkCommandBufferAllocateInfo allocInfo = {};
911 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
912 allocInfo.commandPool = commandPool;
913 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
914 allocInfo.commandBufferCount = (uint32_t) commandBuffers.size();
915
916 if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
917 throw runtime_error("failed to allocate command buffers!");
918 }
919
920 for (size_t i = 0; i < commandBuffers.size(); i++) {
921 VkCommandBufferBeginInfo beginInfo = {};
922 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
923 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
924 beginInfo.pInheritanceInfo = nullptr;
925
926 if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
927 throw runtime_error("failed to begin recording command buffer!");
928 }
929
930 VkRenderPassBeginInfo renderPassInfo = {};
931 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
932 renderPassInfo.renderPass = renderPass;
933 renderPassInfo.framebuffer = swapChainFramebuffers[i];
934 renderPassInfo.renderArea.offset = { 0, 0 };
935 renderPassInfo.renderArea.extent = swapChainExtent;
936
937 array<VkClearValue, 2> clearValues = {};
938 clearValues[0].color = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
939 clearValues[1].depthStencil = { 1.0f, 0 };
940
941 renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
942 renderPassInfo.pClearValues = clearValues.data();
943
944 vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
945
946 modelPipeline.createRenderCommands(commandBuffers[i], i);
947 overlayPipeline.createRenderCommands(commandBuffers[i], i);
948
949 vkCmdEndRenderPass(commandBuffers[i]);
950
951 if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
952 throw runtime_error("failed to record command buffer!");
953 }
954 }
955}
956
957void VulkanGame::createSyncObjects() {
958 imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
959 renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
960 inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
961
962 VkSemaphoreCreateInfo semaphoreInfo = {};
963 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
964
965 VkFenceCreateInfo fenceInfo = {};
966 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
967 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
968
969 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
970 if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS ||
971 vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS ||
972 vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {
973 throw runtime_error("failed to create synchronization objects for a frame!");
974 }
975 }
976}
977
978// TODO: Fix the crash that happens when alt-tabbing
979void VulkanGame::recreateSwapChain() {
980 cout << "Recreating swap chain" << endl;
981 gui->refreshWindowSize();
982
983 while (gui->getWindowWidth() == 0 || gui->getWindowHeight() == 0 ||
984 (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) != 0) {
985 SDL_WaitEvent(nullptr);
986 gui->refreshWindowSize();
987 }
988
989 vkDeviceWaitIdle(device);
990
991 cleanupSwapChain();
992
993 createSwapChain();
994 createImageViews();
995 createRenderPass();
996
997 VulkanUtils::createDepthImage(device, physicalDevice, commandPool, findDepthFormat(), swapChainExtent,
998 depthImage, graphicsQueue);
999 createFramebuffers();
1000
1001 createUniformBuffers<UBO_MvpMat>(uniformBuffers, uniformBuffersMemory, uniformBufferInfoList);
1002
1003 modelPipeline.updateRenderPass(renderPass);
1004 modelPipeline.createPipeline("shaders/scene-vert.spv", "shaders/scene-frag.spv");
1005 modelPipeline.createDescriptorPool(swapChainImages);
1006 modelPipeline.createDescriptorSets(swapChainImages);
1007
1008 overlayPipeline.updateRenderPass(renderPass);
1009 overlayPipeline.createPipeline("shaders/overlay-vert.spv", "shaders/overlay-frag.spv");
1010 overlayPipeline.createDescriptorPool(swapChainImages);
1011 overlayPipeline.createDescriptorSets(swapChainImages);
1012
1013 createCommandBuffers();
1014}
1015
1016void VulkanGame::cleanupSwapChain() {
1017 VulkanUtils::destroyVulkanImage(device, depthImage);
1018
1019 for (VkFramebuffer framebuffer : swapChainFramebuffers) {
1020 vkDestroyFramebuffer(device, framebuffer, nullptr);
1021 }
1022
1023 vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
1024
1025 modelPipeline.cleanup();
1026 overlayPipeline.cleanup();
1027
1028 vkDestroyRenderPass(device, renderPass, nullptr);
1029
1030 for (VkImageView imageView : swapChainImageViews) {
1031 vkDestroyImageView(device, imageView, nullptr);
1032 }
1033
1034 vkDestroySwapchainKHR(device, swapChain, nullptr);
1035
1036 for (size_t i = 0; i < uniformBuffers.size(); i++) {
1037 vkDestroyBuffer(device, uniformBuffers[i], nullptr);
1038 vkFreeMemory(device, uniformBuffersMemory[i], nullptr);
1039 }
1040}
Note: See TracBrowser for help on using the repository browser.