source: opengl-game/vulkan-game.cpp@ 0ae182f

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

In vulkangame, finish implementing recreateSwapChain()

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