source: opengl-game/vulkan-game.cpp@ 7d2b0b9

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

Add and begin implementing a GraphicsPipeline class to hold info for the Vulkan graphics pipeline

  • Property mode set to 100644
File size: 18.9 KB
Line 
1#include "vulkan-game.hpp"
2
3#include <array>
4#include <iostream>
5#include <set>
6
7#include "consts.hpp"
8#include "logger.hpp"
9
10#include "vulkan-utils.hpp"
11
12using namespace std;
13
14VulkanGame::VulkanGame() {
15 gui = nullptr;
16 window = nullptr;
17}
18
19VulkanGame::~VulkanGame() {
20}
21
22void VulkanGame::run(int width, int height, unsigned char guiFlags) {
23 cout << "DEBUGGING IS " << (ENABLE_VALIDATION_LAYERS ? "ON" : "OFF") << endl;
24
25 cout << "Vulkan Game" << endl;
26
27 // This gets the runtime version, use SDL_VERSION() for the comppile-time version
28 // TODO: Create a game-gui function to get the gui version and retrieve it that way
29 SDL_GetVersion(&sdlVersion);
30
31 // TODO: Refactor the logger api to be more flexible,
32 // esp. since gl_log() and gl_log_err() have issues printing anything besides stirngs
33 restart_gl_log();
34 gl_log("starting SDL\n%s.%s.%s",
35 to_string(sdlVersion.major).c_str(),
36 to_string(sdlVersion.minor).c_str(),
37 to_string(sdlVersion.patch).c_str());
38
39 open_log();
40 get_log() << "starting SDL" << endl;
41 get_log() <<
42 (int)sdlVersion.major << "." <<
43 (int)sdlVersion.minor << "." <<
44 (int)sdlVersion.patch << endl;
45
46 if (initWindow(width, height, guiFlags) == RTWO_ERROR) {
47 return;
48 }
49
50 initVulkan();
51 mainLoop();
52 cleanup();
53
54 close_log();
55}
56
57// TODO: Make some more initi functions, or call this initUI if the
58// amount of things initialized here keeps growing
59bool VulkanGame::initWindow(int width, int height, unsigned char guiFlags) {
60 // TODO: Put all fonts, textures, and images in the assets folder
61 gui = new GameGui_SDL();
62
63 if (gui->init() == RTWO_ERROR) {
64 // TODO: Also print these sorts of errors to the log
65 cout << "UI library could not be initialized!" << endl;
66 cout << gui->getError() << endl;
67 return RTWO_ERROR;
68 }
69
70 window = (SDL_Window*) gui->createWindow("Vulkan Game", width, height, guiFlags & GUI_FLAGS_WINDOW_FULLSCREEN);
71 if (window == nullptr) {
72 cout << "Window could not be created!" << endl;
73 cout << gui->getError() << endl;
74 return RTWO_ERROR;
75 }
76
77 cout << "Target window size: (" << width << ", " << height << ")" << endl;
78 cout << "Actual window size: (" << gui->getWindowWidth() << ", " << gui->getWindowHeight() << ")" << endl;
79
80 renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
81 if (renderer == nullptr) {
82 cout << "Renderer could not be created!" << endl;
83 cout << gui->getError() << endl;
84 return RTWO_ERROR;
85 }
86
87 return RTWO_SUCCESS;
88}
89
90void VulkanGame::initVulkan() {
91 const vector<const char*> validationLayers = {
92 "VK_LAYER_KHRONOS_validation"
93 };
94 const vector<const char*> deviceExtensions = {
95 VK_KHR_SWAPCHAIN_EXTENSION_NAME
96 };
97
98 createVulkanInstance(validationLayers);
99 setupDebugMessenger();
100 createVulkanSurface();
101 pickPhysicalDevice(deviceExtensions);
102 createLogicalDevice(validationLayers, deviceExtensions);
103 createSwapChain();
104 createImageViews();
105 createRenderPass();
106 createCommandPool();
107
108 graphicsPipelines.push_back(GraphicsPipeline_Vulkan(device));
109 graphicsPipelines.back().createPipeline("shaders/scene-vert.spv", "shaders/scene-frag.spv");
110
111 graphicsPipelines.push_back(GraphicsPipeline_Vulkan(device));
112 graphicsPipelines.back().createPipeline("shaders/overlay-vert.spv", "shaders/overlay-frag.spv");
113
114 cout << "Created " << graphicsPipelines.size() << " graphics pipelines" << endl;
115}
116
117void VulkanGame::mainLoop() {
118 UIEvent e;
119 bool quit = false;
120
121 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
122
123 while (!quit) {
124 gui->processEvents();
125
126 while (gui->pollEvent(&e)) {
127 switch(e.type) {
128 case UI_EVENT_QUIT:
129 cout << "Quit event detected" << endl;
130 quit = true;
131 break;
132 case UI_EVENT_WINDOW:
133 cout << "Window event detected" << endl;
134 // Currently unused
135 break;
136 case UI_EVENT_WINDOWRESIZE:
137 cout << "Window resize event detected" << endl;
138 framebufferResized = true;
139 break;
140 case UI_EVENT_KEY:
141 if (e.key.keycode == SDL_SCANCODE_ESCAPE) {
142 quit = true;
143 } else {
144 cout << "Key event detected" << endl;
145 }
146 break;
147 case UI_EVENT_MOUSEBUTTONDOWN:
148 cout << "Mouse button down event detected" << endl;
149 break;
150 case UI_EVENT_MOUSEBUTTONUP:
151 cout << "Mouse button up event detected" << endl;
152 break;
153 case UI_EVENT_MOUSEMOTION:
154 break;
155 case UI_EVENT_UNKNOWN:
156 cout << "Unknown event type: 0x" << hex << e.unknown.eventType << dec << endl;
157 break;
158 default:
159 cout << "Unhandled UI event: " << e.type << endl;
160 }
161 }
162
163 renderUI();
164 renderScene();
165 }
166
167 vkDeviceWaitIdle(device);
168}
169
170void VulkanGame::renderUI() {
171 SDL_RenderClear(renderer);
172 SDL_RenderPresent(renderer);
173}
174
175void VulkanGame::renderScene() {
176}
177
178void VulkanGame::cleanup() {
179 cleanupSwapChain();
180
181 vkDestroyCommandPool(device, commandPool, nullptr);
182 vkDestroyDevice(device, nullptr);
183 vkDestroySurfaceKHR(instance, surface, nullptr);
184
185 if (ENABLE_VALIDATION_LAYERS) {
186 VulkanUtils::destroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
187 }
188
189 vkDestroyInstance(instance, nullptr);
190
191 SDL_DestroyRenderer(renderer);
192 renderer = nullptr;
193
194 gui->destroyWindow();
195 gui->shutdown();
196 delete gui;
197}
198
199void VulkanGame::createVulkanInstance(const vector<const char*> &validationLayers) {
200 if (ENABLE_VALIDATION_LAYERS && !VulkanUtils::checkValidationLayerSupport(validationLayers)) {
201 throw runtime_error("validation layers requested, but not available!");
202 }
203
204 VkApplicationInfo appInfo = {};
205 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
206 appInfo.pApplicationName = "Vulkan Game";
207 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
208 appInfo.pEngineName = "No Engine";
209 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
210 appInfo.apiVersion = VK_API_VERSION_1_0;
211
212 VkInstanceCreateInfo createInfo = {};
213 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
214 createInfo.pApplicationInfo = &appInfo;
215
216 vector<const char*> extensions = gui->getRequiredExtensions();
217 if (ENABLE_VALIDATION_LAYERS) {
218 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
219 }
220
221 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
222 createInfo.ppEnabledExtensionNames = extensions.data();
223
224 cout << endl << "Extensions:" << endl;
225 for (const char* extensionName : extensions) {
226 cout << extensionName << endl;
227 }
228 cout << endl;
229
230 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
231 if (ENABLE_VALIDATION_LAYERS) {
232 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
233 createInfo.ppEnabledLayerNames = validationLayers.data();
234
235 populateDebugMessengerCreateInfo(debugCreateInfo);
236 createInfo.pNext = &debugCreateInfo;
237 } else {
238 createInfo.enabledLayerCount = 0;
239
240 createInfo.pNext = nullptr;
241 }
242
243 if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
244 throw runtime_error("failed to create instance!");
245 }
246}
247
248void VulkanGame::setupDebugMessenger() {
249 if (!ENABLE_VALIDATION_LAYERS) return;
250
251 VkDebugUtilsMessengerCreateInfoEXT createInfo;
252 populateDebugMessengerCreateInfo(createInfo);
253
254 if (VulkanUtils::createDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
255 throw runtime_error("failed to set up debug messenger!");
256 }
257}
258
259void VulkanGame::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
260 createInfo = {};
261 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
262 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;
263 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;
264 createInfo.pfnUserCallback = debugCallback;
265}
266
267VKAPI_ATTR VkBool32 VKAPI_CALL VulkanGame::debugCallback(
268 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
269 VkDebugUtilsMessageTypeFlagsEXT messageType,
270 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
271 void* pUserData) {
272 cerr << "validation layer: " << pCallbackData->pMessage << endl;
273
274 return VK_FALSE;
275}
276
277void VulkanGame::createVulkanSurface() {
278 if (gui->createVulkanSurface(instance, &surface) == RTWO_ERROR) {
279 throw runtime_error("failed to create window surface!");
280 }
281}
282
283void VulkanGame::pickPhysicalDevice(const vector<const char*>& deviceExtensions) {
284 uint32_t deviceCount = 0;
285 vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
286
287 if (deviceCount == 0) {
288 throw runtime_error("failed to find GPUs with Vulkan support!");
289 }
290
291 vector<VkPhysicalDevice> devices(deviceCount);
292 vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
293
294 cout << endl << "Graphics cards:" << endl;
295 for (const VkPhysicalDevice& device : devices) {
296 if (isDeviceSuitable(device, deviceExtensions)) {
297 physicalDevice = device;
298 break;
299 }
300 }
301 cout << endl;
302
303 if (physicalDevice == VK_NULL_HANDLE) {
304 throw runtime_error("failed to find a suitable GPU!");
305 }
306}
307
308bool VulkanGame::isDeviceSuitable(VkPhysicalDevice physicalDevice,
309 const vector<const char*>& deviceExtensions) {
310 VkPhysicalDeviceProperties deviceProperties;
311 vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
312
313 cout << "Device: " << deviceProperties.deviceName << endl;
314
315 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
316 bool extensionsSupported = VulkanUtils::checkDeviceExtensionSupport(physicalDevice, deviceExtensions);
317 bool swapChainAdequate = false;
318
319 if (extensionsSupported) {
320 SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
321 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
322 }
323
324 VkPhysicalDeviceFeatures supportedFeatures;
325 vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures);
326
327 return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
328}
329
330void VulkanGame::createLogicalDevice(
331 const vector<const char*> validationLayers,
332 const vector<const char*>& deviceExtensions) {
333 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
334
335 vector<VkDeviceQueueCreateInfo> queueCreateInfos;
336 set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
337
338 float queuePriority = 1.0f;
339 for (uint32_t queueFamily : uniqueQueueFamilies) {
340 VkDeviceQueueCreateInfo queueCreateInfo = {};
341 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
342 queueCreateInfo.queueFamilyIndex = queueFamily;
343 queueCreateInfo.queueCount = 1;
344 queueCreateInfo.pQueuePriorities = &queuePriority;
345
346 queueCreateInfos.push_back(queueCreateInfo);
347 }
348
349 VkPhysicalDeviceFeatures deviceFeatures = {};
350 deviceFeatures.samplerAnisotropy = VK_TRUE;
351
352 VkDeviceCreateInfo createInfo = {};
353 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
354 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
355 createInfo.pQueueCreateInfos = queueCreateInfos.data();
356
357 createInfo.pEnabledFeatures = &deviceFeatures;
358
359 createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
360 createInfo.ppEnabledExtensionNames = deviceExtensions.data();
361
362 // These fields are ignored by up-to-date Vulkan implementations,
363 // but it's a good idea to set them for backwards compatibility
364 if (ENABLE_VALIDATION_LAYERS) {
365 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
366 createInfo.ppEnabledLayerNames = validationLayers.data();
367 } else {
368 createInfo.enabledLayerCount = 0;
369 }
370
371 if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
372 throw runtime_error("failed to create logical device!");
373 }
374
375 vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
376 vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
377}
378
379void VulkanGame::createSwapChain() {
380 SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
381
382 VkSurfaceFormatKHR surfaceFormat = VulkanUtils::chooseSwapSurfaceFormat(swapChainSupport.formats);
383 VkPresentModeKHR presentMode = VulkanUtils::chooseSwapPresentMode(swapChainSupport.presentModes);
384 VkExtent2D extent = VulkanUtils::chooseSwapExtent(swapChainSupport.capabilities, gui->getWindowWidth(), gui->getWindowHeight());
385
386 uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
387 if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) {
388 imageCount = swapChainSupport.capabilities.maxImageCount;
389 }
390
391 VkSwapchainCreateInfoKHR createInfo = {};
392 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
393 createInfo.surface = surface;
394 createInfo.minImageCount = imageCount;
395 createInfo.imageFormat = surfaceFormat.format;
396 createInfo.imageColorSpace = surfaceFormat.colorSpace;
397 createInfo.imageExtent = extent;
398 createInfo.imageArrayLayers = 1;
399 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
400
401 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
402 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
403
404 if (indices.graphicsFamily != indices.presentFamily) {
405 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
406 createInfo.queueFamilyIndexCount = 2;
407 createInfo.pQueueFamilyIndices = queueFamilyIndices;
408 } else {
409 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
410 createInfo.queueFamilyIndexCount = 0;
411 createInfo.pQueueFamilyIndices = nullptr;
412 }
413
414 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
415 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
416 createInfo.presentMode = presentMode;
417 createInfo.clipped = VK_TRUE;
418 createInfo.oldSwapchain = VK_NULL_HANDLE;
419
420 if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
421 throw runtime_error("failed to create swap chain!");
422 }
423
424 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
425 swapChainImages.resize(imageCount);
426 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
427
428 swapChainImageFormat = surfaceFormat.format;
429 swapChainExtent = extent;
430}
431
432void VulkanGame::createImageViews() {
433 swapChainImageViews.resize(swapChainImages.size());
434
435 for (size_t i = 0; i < swapChainImages.size(); i++) {
436 swapChainImageViews[i] = VulkanUtils::createImageView(device, swapChainImages[i], swapChainImageFormat,
437 VK_IMAGE_ASPECT_COLOR_BIT);
438 }
439}
440
441void VulkanGame::createRenderPass() {
442 VkAttachmentDescription colorAttachment = {};
443 colorAttachment.format = swapChainImageFormat;
444 colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
445 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
446 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
447 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
448 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
449 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
450 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
451
452 VkAttachmentReference colorAttachmentRef = {};
453 colorAttachmentRef.attachment = 0;
454 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
455
456 VkAttachmentDescription depthAttachment = {};
457 depthAttachment.format = findDepthFormat();
458 depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
459 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
460 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
461 depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
462 depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
463 depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
464 depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
465
466 VkAttachmentReference depthAttachmentRef = {};
467 depthAttachmentRef.attachment = 1;
468 depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
469
470 VkSubpassDescription subpass = {};
471 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
472 subpass.colorAttachmentCount = 1;
473 subpass.pColorAttachments = &colorAttachmentRef;
474 subpass.pDepthStencilAttachment = &depthAttachmentRef;
475
476 VkSubpassDependency dependency = {};
477 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
478 dependency.dstSubpass = 0;
479 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
480 dependency.srcAccessMask = 0;
481 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
482 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
483
484 array<VkAttachmentDescription, 2> attachments = { colorAttachment, depthAttachment };
485 VkRenderPassCreateInfo renderPassInfo = {};
486 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
487 renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
488 renderPassInfo.pAttachments = attachments.data();
489 renderPassInfo.subpassCount = 1;
490 renderPassInfo.pSubpasses = &subpass;
491 renderPassInfo.dependencyCount = 1;
492 renderPassInfo.pDependencies = &dependency;
493
494 if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
495 throw runtime_error("failed to create render pass!");
496 }
497}
498
499VkFormat VulkanGame::findDepthFormat() {
500 return VulkanUtils::findSupportedFormat(
501 physicalDevice,
502 { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
503 VK_IMAGE_TILING_OPTIMAL,
504 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
505 );
506}
507
508void VulkanGame::createCommandPool() {
509 QueueFamilyIndices queueFamilyIndices = VulkanUtils::findQueueFamilies(physicalDevice, surface);;
510
511 VkCommandPoolCreateInfo poolInfo = {};
512 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
513 poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
514 poolInfo.flags = 0;
515
516 if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
517 throw runtime_error("failed to create graphics command pool!");
518 }
519}
520
521void VulkanGame::cleanupSwapChain() {
522 vkDestroyRenderPass(device, renderPass, nullptr);
523
524 for (auto imageView : swapChainImageViews) {
525 vkDestroyImageView(device, imageView, nullptr);
526 }
527
528 vkDestroySwapchainKHR(device, swapChain, nullptr);
529}
Note: See TracBrowser for help on using the repository browser.