source: opengl-game/vulkan-game.cpp@ 683dd55

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

Add a getObjects() method to the GraphicsPipeline_Vulkan class that returns a reference to the list of objects added to the pipeline, and use that method instead of the numPlanes variable to keep track of the number of textured planes. Also, update the shader compilation batch file and add header files as dependencies to the vulkangame target in the makefile.

  • Property mode set to 100644
File size: 38.7 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 ubo = {};
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
187 createFramebuffers();
188 createUniformBuffers();
189
190 modelPipeline = GraphicsPipeline_Vulkan<ModelVertex>(physicalDevice, device, renderPass,
191 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, 16, 24);
192
193 modelPipeline.addObject({
194 {{-0.5f, -0.5f, -2.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
195 {{ 0.5f, -0.5f, -2.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
196 {{ 0.5f, 0.5f, -2.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
197 {{-0.5f, 0.5f, -2.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
198 }, {
199 0, 1, 2, 2, 3, 0
200 }, commandPool, graphicsQueue);
201
202 modelPipeline.addObject({
203 {{-0.5f, -0.5f, -1.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
204 {{ 0.5f, -0.5f, -1.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
205 {{ 0.5f, 0.5f, -1.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
206 {{-0.5f, 0.5f, -1.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
207 }, {
208 0, 1, 2, 2, 3, 0
209 }, commandPool, graphicsQueue);
210
211 modelPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ModelVertex::pos));
212 modelPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&ModelVertex::color));
213 modelPipeline.addAttribute(VK_FORMAT_R32G32_SFLOAT, offset_of(&ModelVertex::texCoord));
214
215 modelPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
216 VK_SHADER_STAGE_VERTEX_BIT, &uniformBufferInfoList);
217 modelPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
218 VK_SHADER_STAGE_FRAGMENT_BIT, &floorTextureImageDescriptor);
219
220 modelPipeline.createDescriptorSetLayout();
221 modelPipeline.createPipeline("shaders/scene-vert.spv", "shaders/scene-frag.spv");
222 modelPipeline.createDescriptorPool(swapChainImages);
223 modelPipeline.createDescriptorSets(swapChainImages);
224
225 overlayPipeline = GraphicsPipeline_Vulkan<OverlayVertex>(physicalDevice, device, renderPass,
226 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, 4, 6);
227
228 overlayPipeline.addObject({
229 {{-1.0f, 1.0f, 0.0f}, {0.0f, 1.0f}},
230 {{ 1.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
231 {{ 1.0f, -1.0f, 0.0f}, {1.0f, 0.0f}},
232 {{-1.0f, -1.0f, 0.0f}, {0.0f, 0.0f}}
233 }, {
234 0, 1, 2, 2, 3, 0
235 }, commandPool, graphicsQueue);
236
237 overlayPipeline.addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&OverlayVertex::pos));
238 overlayPipeline.addAttribute(VK_FORMAT_R32G32_SFLOAT, offset_of(&OverlayVertex::texCoord));
239
240 overlayPipeline.addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
241 VK_SHADER_STAGE_FRAGMENT_BIT, &sdlOverlayImageDescriptor);
242
243 overlayPipeline.createDescriptorSetLayout();
244 overlayPipeline.createPipeline("shaders/overlay-vert.spv", "shaders/overlay-frag.spv");
245 overlayPipeline.createDescriptorPool(swapChainImages);
246 overlayPipeline.createDescriptorSets(swapChainImages);
247
248 cout << "Created all the graphics pipelines" << endl;
249
250 createCommandBuffers();
251
252 createSyncObjects();
253}
254
255// TODO: Maybe changes the name to initScene() or something similar
256void VulkanGame::initMatrices() {
257 cam_pos = vec3(0.0f, 0.0f, 2.0f);
258
259 float cam_yaw = 0.0f;
260 float cam_pitch = -50.0f;
261
262 mat4 yaw_mat = rotate(mat4(1.0f), radians(-cam_yaw), vec3(0.0f, 1.0f, 0.0f));
263 mat4 pitch_mat = rotate(mat4(1.0f), radians(-cam_pitch), vec3(1.0f, 0.0f, 0.0f));
264
265 mat4 R = pitch_mat * yaw_mat;
266 mat4 T = translate(mat4(1.0f), vec3(-cam_pos.x, -cam_pos.y, -cam_pos.z));
267
268 ubo.view = R * T;
269
270 ubo.proj = perspective(radians(FOV_ANGLE), (float)swapChainExtent.width / (float)swapChainExtent.height, NEAR_CLIP, FAR_CLIP);
271 ubo.proj[1][1] *= -1; // flip the y-axis so that +y is up
272}
273
274void VulkanGame::mainLoop() {
275 UIEvent e;
276 bool quit = false;
277
278 while (!quit) {
279 gui->processEvents();
280
281 while (gui->pollEvent(&e)) {
282 switch(e.type) {
283 case UI_EVENT_QUIT:
284 cout << "Quit event detected" << endl;
285 quit = true;
286 break;
287 case UI_EVENT_WINDOW:
288 cout << "Window event detected" << endl;
289 // Currently unused
290 break;
291 case UI_EVENT_WINDOWRESIZE:
292 cout << "Window resize event detected" << endl;
293 framebufferResized = true;
294 break;
295 case UI_EVENT_KEYDOWN:
296 if (e.key.keycode == SDL_SCANCODE_ESCAPE) {
297 quit = true;
298 } else if (e.key.keycode == SDL_SCANCODE_SPACE) {
299 cout << "Adding a plane" << endl;
300 float zOffset = -2.0f + (0.5f * modelPipeline.getObjects().size());
301
302 vkDeviceWaitIdle(device);
303 vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
304
305 modelPipeline.addObject({
306 {{-0.5f, -0.5f, zOffset}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
307 {{ 0.5f, -0.5f, zOffset}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
308 {{ 0.5f, 0.5f, zOffset}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
309 {{-0.5f, 0.5f, zOffset}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
310 }, {
311 0, 1, 2, 2, 3, 0
312 }, commandPool, graphicsQueue);
313
314 createCommandBuffers();
315 } else {
316 cout << "Key event detected" << endl;
317 }
318 break;
319 case UI_EVENT_KEYUP:
320 break;
321 case UI_EVENT_MOUSEBUTTONDOWN:
322 cout << "Mouse button down event detected" << endl;
323 break;
324 case UI_EVENT_MOUSEBUTTONUP:
325 cout << "Mouse button up event detected" << endl;
326 break;
327 case UI_EVENT_MOUSEMOTION:
328 break;
329 case UI_EVENT_UNKNOWN:
330 cout << "Unknown event type: 0x" << hex << e.unknown.eventType << dec << endl;
331 break;
332 default:
333 cout << "Unhandled UI event: " << e.type << endl;
334 }
335 }
336
337 renderUI();
338 renderScene();
339 }
340
341 vkDeviceWaitIdle(device);
342}
343
344void VulkanGame::renderUI() {
345 SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
346 SDL_RenderClear(renderer);
347
348 SDL_Rect rect = {280, 220, 100, 100};
349 SDL_SetRenderDrawColor(renderer, 0x00, 0xFF, 0x00, 0xFF);
350 SDL_RenderFillRect(renderer, &rect);
351
352 rect = {10, 10, 0, 0};
353 SDL_QueryTexture(fontSDLTexture, nullptr, nullptr, &(rect.w), &(rect.h));
354 SDL_RenderCopy(renderer, fontSDLTexture, nullptr, &rect);
355
356 rect = {10, 80, 0, 0};
357 SDL_QueryTexture(imageSDLTexture, nullptr, nullptr, &(rect.w), &(rect.h));
358 SDL_RenderCopy(renderer, imageSDLTexture, nullptr, &rect);
359
360 SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0xFF, 0xFF);
361 SDL_RenderDrawLine(renderer, 50, 5, 150, 500);
362
363 VulkanUtils::populateVulkanImageFromSDLTexture(device, physicalDevice, commandPool, uiOverlay, renderer,
364 sdlOverlayImage, graphicsQueue);
365}
366
367void VulkanGame::renderScene() {
368 vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, numeric_limits<uint64_t>::max());
369
370 uint32_t imageIndex;
371
372 VkResult result = vkAcquireNextImageKHR(device, swapChain, numeric_limits<uint64_t>::max(),
373 imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
374
375 if (result == VK_ERROR_OUT_OF_DATE_KHR) {
376 recreateSwapChain();
377 return;
378 } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
379 throw runtime_error("failed to acquire swap chain image!");
380 }
381
382 updateUniformBuffer(imageIndex);
383
384 VkSubmitInfo submitInfo = {};
385 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
386
387 VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] };
388 VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
389
390 submitInfo.waitSemaphoreCount = 1;
391 submitInfo.pWaitSemaphores = waitSemaphores;
392 submitInfo.pWaitDstStageMask = waitStages;
393 submitInfo.commandBufferCount = 1;
394 submitInfo.pCommandBuffers = &commandBuffers[imageIndex];
395
396 VkSemaphore signalSemaphores[] = { renderFinishedSemaphores[currentFrame] };
397
398 submitInfo.signalSemaphoreCount = 1;
399 submitInfo.pSignalSemaphores = signalSemaphores;
400
401 vkResetFences(device, 1, &inFlightFences[currentFrame]);
402
403 if (vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) {
404 throw runtime_error("failed to submit draw command buffer!");
405 }
406
407 VkPresentInfoKHR presentInfo = {};
408 presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
409 presentInfo.waitSemaphoreCount = 1;
410 presentInfo.pWaitSemaphores = signalSemaphores;
411
412 VkSwapchainKHR swapChains[] = { swapChain };
413 presentInfo.swapchainCount = 1;
414 presentInfo.pSwapchains = swapChains;
415 presentInfo.pImageIndices = &imageIndex;
416 presentInfo.pResults = nullptr;
417
418 result = vkQueuePresentKHR(presentQueue, &presentInfo);
419
420 if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || framebufferResized) {
421 framebufferResized = false;
422 recreateSwapChain();
423 } else if (result != VK_SUCCESS) {
424 throw runtime_error("failed to present swap chain image!");
425 }
426
427 currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
428 currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
429}
430
431void VulkanGame::cleanup() {
432 cleanupSwapChain();
433
434 VulkanUtils::destroyVulkanImage(device, floorTextureImage);
435 VulkanUtils::destroyVulkanImage(device, sdlOverlayImage);
436
437 vkDestroySampler(device, textureSampler, nullptr);
438
439 modelPipeline.cleanupBuffers();
440 overlayPipeline.cleanupBuffers();
441
442 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
443 vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
444 vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
445 vkDestroyFence(device, inFlightFences[i], nullptr);
446 }
447
448 vkDestroyCommandPool(device, commandPool, nullptr);
449 vkDestroyDevice(device, nullptr);
450 vkDestroySurfaceKHR(instance, surface, nullptr);
451
452 if (ENABLE_VALIDATION_LAYERS) {
453 VulkanUtils::destroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
454 }
455
456 vkDestroyInstance(instance, nullptr);
457
458 // TODO: Check if any of these functions accept null parameters
459 // If they do, I don't need to check for that
460
461 if (uiOverlay != nullptr) {
462 SDL_DestroyTexture(uiOverlay);
463 uiOverlay = nullptr;
464 }
465
466 if (fontSDLTexture != nullptr) {
467 SDL_DestroyTexture(fontSDLTexture);
468 fontSDLTexture = nullptr;
469 }
470
471 if (imageSDLTexture != nullptr) {
472 SDL_DestroyTexture(imageSDLTexture);
473 imageSDLTexture = nullptr;
474 }
475
476 TTF_CloseFont(font);
477 font = nullptr;
478
479 SDL_DestroyRenderer(renderer);
480 renderer = nullptr;
481
482 gui->destroyWindow();
483 gui->shutdown();
484 delete gui;
485}
486
487void VulkanGame::createVulkanInstance(const vector<const char*> &validationLayers) {
488 if (ENABLE_VALIDATION_LAYERS && !VulkanUtils::checkValidationLayerSupport(validationLayers)) {
489 throw runtime_error("validation layers requested, but not available!");
490 }
491
492 VkApplicationInfo appInfo = {};
493 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
494 appInfo.pApplicationName = "Vulkan Game";
495 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
496 appInfo.pEngineName = "No Engine";
497 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
498 appInfo.apiVersion = VK_API_VERSION_1_0;
499
500 VkInstanceCreateInfo createInfo = {};
501 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
502 createInfo.pApplicationInfo = &appInfo;
503
504 vector<const char*> extensions = gui->getRequiredExtensions();
505 if (ENABLE_VALIDATION_LAYERS) {
506 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
507 }
508
509 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
510 createInfo.ppEnabledExtensionNames = extensions.data();
511
512 cout << endl << "Extensions:" << endl;
513 for (const char* extensionName : extensions) {
514 cout << extensionName << endl;
515 }
516 cout << endl;
517
518 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
519 if (ENABLE_VALIDATION_LAYERS) {
520 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
521 createInfo.ppEnabledLayerNames = validationLayers.data();
522
523 populateDebugMessengerCreateInfo(debugCreateInfo);
524 createInfo.pNext = &debugCreateInfo;
525 } else {
526 createInfo.enabledLayerCount = 0;
527
528 createInfo.pNext = nullptr;
529 }
530
531 if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
532 throw runtime_error("failed to create instance!");
533 }
534}
535
536void VulkanGame::setupDebugMessenger() {
537 if (!ENABLE_VALIDATION_LAYERS) return;
538
539 VkDebugUtilsMessengerCreateInfoEXT createInfo;
540 populateDebugMessengerCreateInfo(createInfo);
541
542 if (VulkanUtils::createDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
543 throw runtime_error("failed to set up debug messenger!");
544 }
545}
546
547void VulkanGame::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
548 createInfo = {};
549 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
550 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;
551 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;
552 createInfo.pfnUserCallback = debugCallback;
553}
554
555VKAPI_ATTR VkBool32 VKAPI_CALL VulkanGame::debugCallback(
556 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
557 VkDebugUtilsMessageTypeFlagsEXT messageType,
558 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
559 void* pUserData) {
560 cerr << "validation layer: " << pCallbackData->pMessage << endl;
561
562 return VK_FALSE;
563}
564
565void VulkanGame::createVulkanSurface() {
566 if (gui->createVulkanSurface(instance, &surface) == RTWO_ERROR) {
567 throw runtime_error("failed to create window surface!");
568 }
569}
570
571void VulkanGame::pickPhysicalDevice(const vector<const char*>& deviceExtensions) {
572 uint32_t deviceCount = 0;
573 vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
574
575 if (deviceCount == 0) {
576 throw runtime_error("failed to find GPUs with Vulkan support!");
577 }
578
579 vector<VkPhysicalDevice> devices(deviceCount);
580 vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
581
582 cout << endl << "Graphics cards:" << endl;
583 for (const VkPhysicalDevice& device : devices) {
584 if (isDeviceSuitable(device, deviceExtensions)) {
585 physicalDevice = device;
586 break;
587 }
588 }
589 cout << endl;
590
591 if (physicalDevice == VK_NULL_HANDLE) {
592 throw runtime_error("failed to find a suitable GPU!");
593 }
594}
595
596bool VulkanGame::isDeviceSuitable(VkPhysicalDevice physicalDevice,
597 const vector<const char*>& deviceExtensions) {
598 VkPhysicalDeviceProperties deviceProperties;
599 vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
600
601 cout << "Device: " << deviceProperties.deviceName << endl;
602
603 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
604 bool extensionsSupported = VulkanUtils::checkDeviceExtensionSupport(physicalDevice, deviceExtensions);
605 bool swapChainAdequate = false;
606
607 if (extensionsSupported) {
608 SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
609 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
610 }
611
612 VkPhysicalDeviceFeatures supportedFeatures;
613 vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures);
614
615 return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
616}
617
618void VulkanGame::createLogicalDevice(
619 const vector<const char*> validationLayers,
620 const vector<const char*>& deviceExtensions) {
621 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
622
623 vector<VkDeviceQueueCreateInfo> queueCreateInfoList;
624 set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
625
626 float queuePriority = 1.0f;
627 for (uint32_t queueFamily : uniqueQueueFamilies) {
628 VkDeviceQueueCreateInfo queueCreateInfo = {};
629 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
630 queueCreateInfo.queueFamilyIndex = queueFamily;
631 queueCreateInfo.queueCount = 1;
632 queueCreateInfo.pQueuePriorities = &queuePriority;
633
634 queueCreateInfoList.push_back(queueCreateInfo);
635 }
636
637 VkPhysicalDeviceFeatures deviceFeatures = {};
638 deviceFeatures.samplerAnisotropy = VK_TRUE;
639
640 VkDeviceCreateInfo createInfo = {};
641 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
642 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfoList.size());
643 createInfo.pQueueCreateInfos = queueCreateInfoList.data();
644
645 createInfo.pEnabledFeatures = &deviceFeatures;
646
647 createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
648 createInfo.ppEnabledExtensionNames = deviceExtensions.data();
649
650 // These fields are ignored by up-to-date Vulkan implementations,
651 // but it's a good idea to set them for backwards compatibility
652 if (ENABLE_VALIDATION_LAYERS) {
653 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
654 createInfo.ppEnabledLayerNames = validationLayers.data();
655 } else {
656 createInfo.enabledLayerCount = 0;
657 }
658
659 if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
660 throw runtime_error("failed to create logical device!");
661 }
662
663 vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
664 vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
665}
666
667void VulkanGame::createSwapChain() {
668 SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
669
670 VkSurfaceFormatKHR surfaceFormat = VulkanUtils::chooseSwapSurfaceFormat(swapChainSupport.formats);
671 VkPresentModeKHR presentMode = VulkanUtils::chooseSwapPresentMode(swapChainSupport.presentModes);
672 VkExtent2D extent = VulkanUtils::chooseSwapExtent(swapChainSupport.capabilities, gui->getWindowWidth(), gui->getWindowHeight());
673
674 uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
675 if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) {
676 imageCount = swapChainSupport.capabilities.maxImageCount;
677 }
678
679 VkSwapchainCreateInfoKHR createInfo = {};
680 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
681 createInfo.surface = surface;
682 createInfo.minImageCount = imageCount;
683 createInfo.imageFormat = surfaceFormat.format;
684 createInfo.imageColorSpace = surfaceFormat.colorSpace;
685 createInfo.imageExtent = extent;
686 createInfo.imageArrayLayers = 1;
687 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
688
689 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
690 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
691
692 if (indices.graphicsFamily != indices.presentFamily) {
693 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
694 createInfo.queueFamilyIndexCount = 2;
695 createInfo.pQueueFamilyIndices = queueFamilyIndices;
696 } else {
697 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
698 createInfo.queueFamilyIndexCount = 0;
699 createInfo.pQueueFamilyIndices = nullptr;
700 }
701
702 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
703 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
704 createInfo.presentMode = presentMode;
705 createInfo.clipped = VK_TRUE;
706 createInfo.oldSwapchain = VK_NULL_HANDLE;
707
708 if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
709 throw runtime_error("failed to create swap chain!");
710 }
711
712 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
713 swapChainImages.resize(imageCount);
714 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
715
716 swapChainImageFormat = surfaceFormat.format;
717 swapChainExtent = extent;
718}
719
720void VulkanGame::createImageViews() {
721 swapChainImageViews.resize(swapChainImages.size());
722
723 for (size_t i = 0; i < swapChainImages.size(); i++) {
724 swapChainImageViews[i] = VulkanUtils::createImageView(device, swapChainImages[i], swapChainImageFormat,
725 VK_IMAGE_ASPECT_COLOR_BIT);
726 }
727}
728
729void VulkanGame::createRenderPass() {
730 VkAttachmentDescription colorAttachment = {};
731 colorAttachment.format = swapChainImageFormat;
732 colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
733 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
734 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
735 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
736 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
737 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
738 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
739
740 VkAttachmentReference colorAttachmentRef = {};
741 colorAttachmentRef.attachment = 0;
742 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
743
744 VkAttachmentDescription depthAttachment = {};
745 depthAttachment.format = findDepthFormat();
746 depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
747 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
748 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
749 depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
750 depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
751 depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
752 depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
753
754 VkAttachmentReference depthAttachmentRef = {};
755 depthAttachmentRef.attachment = 1;
756 depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
757
758 VkSubpassDescription subpass = {};
759 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
760 subpass.colorAttachmentCount = 1;
761 subpass.pColorAttachments = &colorAttachmentRef;
762 subpass.pDepthStencilAttachment = &depthAttachmentRef;
763
764 VkSubpassDependency dependency = {};
765 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
766 dependency.dstSubpass = 0;
767 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
768 dependency.srcAccessMask = 0;
769 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
770 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
771
772 array<VkAttachmentDescription, 2> attachments = { colorAttachment, depthAttachment };
773 VkRenderPassCreateInfo renderPassInfo = {};
774 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
775 renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
776 renderPassInfo.pAttachments = attachments.data();
777 renderPassInfo.subpassCount = 1;
778 renderPassInfo.pSubpasses = &subpass;
779 renderPassInfo.dependencyCount = 1;
780 renderPassInfo.pDependencies = &dependency;
781
782 if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
783 throw runtime_error("failed to create render pass!");
784 }
785}
786
787VkFormat VulkanGame::findDepthFormat() {
788 return VulkanUtils::findSupportedFormat(
789 physicalDevice,
790 { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
791 VK_IMAGE_TILING_OPTIMAL,
792 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
793 );
794}
795
796void VulkanGame::createCommandPool() {
797 QueueFamilyIndices queueFamilyIndices = VulkanUtils::findQueueFamilies(physicalDevice, surface);;
798
799 VkCommandPoolCreateInfo poolInfo = {};
800 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
801 poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
802 poolInfo.flags = 0;
803
804 if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
805 throw runtime_error("failed to create graphics command pool!");
806 }
807}
808
809void VulkanGame::createImageResources() {
810 VulkanUtils::createDepthImage(device, physicalDevice, commandPool, findDepthFormat(), swapChainExtent,
811 depthImage, graphicsQueue);
812
813 createTextureSampler();
814
815 VulkanUtils::createVulkanImageFromFile(device, physicalDevice, commandPool, "textures/texture.jpg",
816 floorTextureImage, graphicsQueue);
817
818 floorTextureImageDescriptor = {};
819 floorTextureImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
820 floorTextureImageDescriptor.imageView = floorTextureImage.imageView;
821 floorTextureImageDescriptor.sampler = textureSampler;
822
823 VulkanUtils::createVulkanImageFromSDLTexture(device, physicalDevice, uiOverlay, sdlOverlayImage);
824
825 sdlOverlayImageDescriptor = {};
826 sdlOverlayImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
827 sdlOverlayImageDescriptor.imageView = sdlOverlayImage.imageView;
828 sdlOverlayImageDescriptor.sampler = textureSampler;
829}
830
831void VulkanGame::createTextureSampler() {
832 VkSamplerCreateInfo samplerInfo = {};
833 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
834 samplerInfo.magFilter = VK_FILTER_LINEAR;
835 samplerInfo.minFilter = VK_FILTER_LINEAR;
836
837 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
838 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
839 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
840
841 samplerInfo.anisotropyEnable = VK_TRUE;
842 samplerInfo.maxAnisotropy = 16;
843 samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
844 samplerInfo.unnormalizedCoordinates = VK_FALSE;
845 samplerInfo.compareEnable = VK_FALSE;
846 samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
847 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
848 samplerInfo.mipLodBias = 0.0f;
849 samplerInfo.minLod = 0.0f;
850 samplerInfo.maxLod = 0.0f;
851
852 if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
853 throw runtime_error("failed to create texture sampler!");
854 }
855}
856
857void VulkanGame::createFramebuffers() {
858 swapChainFramebuffers.resize(swapChainImageViews.size());
859
860 for (size_t i = 0; i < swapChainImageViews.size(); i++) {
861 array<VkImageView, 2> attachments = {
862 swapChainImageViews[i],
863 depthImage.imageView
864 };
865
866 VkFramebufferCreateInfo framebufferInfo = {};
867 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
868 framebufferInfo.renderPass = renderPass;
869 framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
870 framebufferInfo.pAttachments = attachments.data();
871 framebufferInfo.width = swapChainExtent.width;
872 framebufferInfo.height = swapChainExtent.height;
873 framebufferInfo.layers = 1;
874
875 if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
876 throw runtime_error("failed to create framebuffer!");
877 }
878 }
879}
880
881void VulkanGame::createUniformBuffers() {
882 VkDeviceSize bufferSize = sizeof(UniformBufferObject);
883
884 uniformBuffers.resize(swapChainImages.size());
885 uniformBuffersMemory.resize(swapChainImages.size());
886 uniformBufferInfoList.resize(swapChainImages.size());
887
888 for (size_t i = 0; i < swapChainImages.size(); i++) {
889 VulkanUtils::createBuffer(device, physicalDevice, bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
890 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
891 uniformBuffers[i], uniformBuffersMemory[i]);
892
893 uniformBufferInfoList[i].buffer = uniformBuffers[i];
894 uniformBufferInfoList[i].offset = 0;
895 uniformBufferInfoList[i].range = sizeof(UniformBufferObject);
896 }
897}
898
899void VulkanGame::createCommandBuffers() {
900 commandBuffers.resize(swapChainImages.size());
901
902 VkCommandBufferAllocateInfo allocInfo = {};
903 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
904 allocInfo.commandPool = commandPool;
905 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
906 allocInfo.commandBufferCount = (uint32_t) commandBuffers.size();
907
908 if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
909 throw runtime_error("failed to allocate command buffers!");
910 }
911
912 for (size_t i = 0; i < commandBuffers.size(); i++) {
913 VkCommandBufferBeginInfo beginInfo = {};
914 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
915 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
916 beginInfo.pInheritanceInfo = nullptr;
917
918 if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
919 throw runtime_error("failed to begin recording command buffer!");
920 }
921
922 VkRenderPassBeginInfo renderPassInfo = {};
923 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
924 renderPassInfo.renderPass = renderPass;
925 renderPassInfo.framebuffer = swapChainFramebuffers[i];
926 renderPassInfo.renderArea.offset = { 0, 0 };
927 renderPassInfo.renderArea.extent = swapChainExtent;
928
929 array<VkClearValue, 2> clearValues = {};
930 clearValues[0].color = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
931 clearValues[1].depthStencil = { 1.0f, 0 };
932
933 renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
934 renderPassInfo.pClearValues = clearValues.data();
935
936 vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
937
938 modelPipeline.createRenderCommands(commandBuffers[i], i);
939 overlayPipeline.createRenderCommands(commandBuffers[i], i);
940
941 vkCmdEndRenderPass(commandBuffers[i]);
942
943 if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
944 throw runtime_error("failed to record command buffer!");
945 }
946 }
947}
948
949void VulkanGame::createSyncObjects() {
950 imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
951 renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
952 inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
953
954 VkSemaphoreCreateInfo semaphoreInfo = {};
955 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
956
957 VkFenceCreateInfo fenceInfo = {};
958 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
959 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
960
961 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
962 if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS ||
963 vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS ||
964 vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {
965 throw runtime_error("failed to create synchronization objects for a frame!");
966 }
967 }
968}
969
970// TODO: Fix the crash that happens when alt-tabbing
971void VulkanGame::recreateSwapChain() {
972 cout << "Recreating swap chain" << endl;
973 gui->refreshWindowSize();
974
975 while (gui->getWindowWidth() == 0 || gui->getWindowHeight() == 0 ||
976 (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) != 0) {
977 SDL_WaitEvent(nullptr);
978 gui->refreshWindowSize();
979 }
980
981 vkDeviceWaitIdle(device);
982
983 cleanupSwapChain();
984
985 createSwapChain();
986 createImageViews();
987 createRenderPass();
988
989 VulkanUtils::createDepthImage(device, physicalDevice, commandPool, findDepthFormat(), swapChainExtent,
990 depthImage, graphicsQueue);
991 createFramebuffers();
992 createUniformBuffers();
993
994 modelPipeline.updateRenderPass(renderPass);
995 modelPipeline.createPipeline("shaders/scene-vert.spv", "shaders/scene-frag.spv");
996 modelPipeline.createDescriptorPool(swapChainImages);
997 modelPipeline.createDescriptorSets(swapChainImages);
998
999 overlayPipeline.updateRenderPass(renderPass);
1000 overlayPipeline.createPipeline("shaders/overlay-vert.spv", "shaders/overlay-frag.spv");
1001 overlayPipeline.createDescriptorPool(swapChainImages);
1002 overlayPipeline.createDescriptorSets(swapChainImages);
1003
1004 createCommandBuffers();
1005}
1006
1007void VulkanGame::updateUniformBuffer(uint32_t currentImage) {
1008 static auto startTime = chrono::high_resolution_clock::now();
1009
1010 auto currentTime = chrono::high_resolution_clock::now();
1011 float time = chrono::duration<float, chrono::seconds::period>(currentTime - startTime).count();
1012
1013 ubo.model =
1014 translate(mat4(1.0f), vec3(0.0f, -2.0f, -0.0f)) *
1015 rotate(mat4(1.0f), time * radians(90.0f), vec3(0.0f, 0.0f, 1.0f));
1016
1017 void* data;
1018 vkMapMemory(device, uniformBuffersMemory[currentImage], 0, sizeof(ubo), 0, &data);
1019 memcpy(data, &ubo, sizeof(ubo));
1020 vkUnmapMemory(device, uniformBuffersMemory[currentImage]);
1021}
1022
1023void VulkanGame::cleanupSwapChain() {
1024 VulkanUtils::destroyVulkanImage(device, depthImage);
1025
1026 for (VkFramebuffer framebuffer : swapChainFramebuffers) {
1027 vkDestroyFramebuffer(device, framebuffer, nullptr);
1028 }
1029
1030 vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
1031
1032 modelPipeline.cleanup();
1033 overlayPipeline.cleanup();
1034
1035 vkDestroyRenderPass(device, renderPass, nullptr);
1036
1037 for (VkImageView imageView : swapChainImageViews) {
1038 vkDestroyImageView(device, imageView, nullptr);
1039 }
1040
1041 vkDestroySwapchainKHR(device, swapChain, nullptr);
1042
1043 for (size_t i = 0; i < uniformBuffers.size(); i++) {
1044 vkDestroyBuffer(device, uniformBuffers[i], nullptr);
1045 vkFreeMemory(device, uniformBuffersMemory[i], nullptr);
1046 }
1047}
Note: See TracBrowser for help on using the repository browser.