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

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

In vulkangame, create the command pool

  • Property mode set to 100644
File size: 18.4 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
109void VulkanGame::mainLoop() {
110 UIEvent e;
111 bool quit = false;
112
113 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
114
115 while (!quit) {
116 gui->processEvents();
117
118 while (gui->pollEvent(&e)) {
119 switch(e.type) {
120 case UI_EVENT_QUIT:
121 cout << "Quit event detected" << endl;
122 quit = true;
123 break;
124 case UI_EVENT_WINDOW:
125 cout << "Window event detected" << endl;
126 // Currently unused
127 break;
128 case UI_EVENT_WINDOWRESIZE:
129 cout << "Window resize event detected" << endl;
130 framebufferResized = true;
131 break;
132 case UI_EVENT_KEY:
133 if (e.key.keycode == SDL_SCANCODE_ESCAPE) {
134 quit = true;
135 } else {
136 cout << "Key event detected" << endl;
137 }
138 break;
139 case UI_EVENT_MOUSEBUTTONDOWN:
140 cout << "Mouse button down event detected" << endl;
141 break;
142 case UI_EVENT_MOUSEBUTTONUP:
143 cout << "Mouse button up event detected" << endl;
144 break;
145 case UI_EVENT_MOUSEMOTION:
146 break;
147 default:
148 cout << "Unhandled UI event: " << e.type << endl;
149 }
150 }
151
152 renderUI();
153 renderScene();
154 }
155
156 vkDeviceWaitIdle(device);
157}
158
159void VulkanGame::renderUI() {
160 SDL_RenderClear(renderer);
161 SDL_RenderPresent(renderer);
162}
163
164void VulkanGame::renderScene() {
165}
166
167void VulkanGame::cleanup() {
168 cleanupSwapChain();
169
170 vkDestroyCommandPool(device, commandPool, nullptr);
171 vkDestroyDevice(device, nullptr);
172 vkDestroySurfaceKHR(instance, surface, nullptr);
173
174 if (ENABLE_VALIDATION_LAYERS) {
175 VulkanUtils::destroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
176 }
177
178 vkDestroyInstance(instance, nullptr);
179
180 SDL_DestroyRenderer(renderer);
181 renderer = nullptr;
182
183 gui->destroyWindow();
184 gui->shutdown();
185 delete gui;
186}
187
188void VulkanGame::createVulkanInstance(const vector<const char*> &validationLayers) {
189 if (ENABLE_VALIDATION_LAYERS && !VulkanUtils::checkValidationLayerSupport(validationLayers)) {
190 throw runtime_error("validation layers requested, but not available!");
191 }
192
193 VkApplicationInfo appInfo = {};
194 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
195 appInfo.pApplicationName = "Vulkan Game";
196 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
197 appInfo.pEngineName = "No Engine";
198 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
199 appInfo.apiVersion = VK_API_VERSION_1_0;
200
201 VkInstanceCreateInfo createInfo = {};
202 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
203 createInfo.pApplicationInfo = &appInfo;
204
205 vector<const char*> extensions = gui->getRequiredExtensions();
206 if (ENABLE_VALIDATION_LAYERS) {
207 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
208 }
209
210 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
211 createInfo.ppEnabledExtensionNames = extensions.data();
212
213 cout << endl << "Extensions:" << endl;
214 for (const char* extensionName : extensions) {
215 cout << extensionName << endl;
216 }
217 cout << endl;
218
219 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
220 if (ENABLE_VALIDATION_LAYERS) {
221 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
222 createInfo.ppEnabledLayerNames = validationLayers.data();
223
224 populateDebugMessengerCreateInfo(debugCreateInfo);
225 createInfo.pNext = &debugCreateInfo;
226 } else {
227 createInfo.enabledLayerCount = 0;
228
229 createInfo.pNext = nullptr;
230 }
231
232 if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
233 throw runtime_error("failed to create instance!");
234 }
235}
236
237void VulkanGame::setupDebugMessenger() {
238 if (!ENABLE_VALIDATION_LAYERS) return;
239
240 VkDebugUtilsMessengerCreateInfoEXT createInfo;
241 populateDebugMessengerCreateInfo(createInfo);
242
243 if (VulkanUtils::createDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
244 throw runtime_error("failed to set up debug messenger!");
245 }
246}
247
248void VulkanGame::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
249 createInfo = {};
250 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
251 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;
252 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;
253 createInfo.pfnUserCallback = debugCallback;
254}
255
256VKAPI_ATTR VkBool32 VKAPI_CALL VulkanGame::debugCallback(
257 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
258 VkDebugUtilsMessageTypeFlagsEXT messageType,
259 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
260 void* pUserData) {
261 cerr << "validation layer: " << pCallbackData->pMessage << endl;
262
263 return VK_FALSE;
264}
265
266void VulkanGame::createVulkanSurface() {
267 if (gui->createVulkanSurface(instance, &surface) == RTWO_ERROR) {
268 throw runtime_error("failed to create window surface!");
269 }
270}
271
272void VulkanGame::pickPhysicalDevice(const vector<const char*>& deviceExtensions) {
273 uint32_t deviceCount = 0;
274 vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
275
276 if (deviceCount == 0) {
277 throw runtime_error("failed to find GPUs with Vulkan support!");
278 }
279
280 vector<VkPhysicalDevice> devices(deviceCount);
281 vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
282
283 cout << endl << "Graphics cards:" << endl;
284 for (const VkPhysicalDevice& device : devices) {
285 if (isDeviceSuitable(device, deviceExtensions)) {
286 physicalDevice = device;
287 break;
288 }
289 }
290 cout << endl;
291
292 if (physicalDevice == VK_NULL_HANDLE) {
293 throw runtime_error("failed to find a suitable GPU!");
294 }
295}
296
297bool VulkanGame::isDeviceSuitable(VkPhysicalDevice physicalDevice,
298 const vector<const char*>& deviceExtensions) {
299 VkPhysicalDeviceProperties deviceProperties;
300 vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
301
302 cout << "Device: " << deviceProperties.deviceName << endl;
303
304 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
305 bool extensionsSupported = VulkanUtils::checkDeviceExtensionSupport(physicalDevice, deviceExtensions);
306 bool swapChainAdequate = false;
307
308 if (extensionsSupported) {
309 SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
310 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
311 }
312
313 VkPhysicalDeviceFeatures supportedFeatures;
314 vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures);
315
316 return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
317}
318
319void VulkanGame::createLogicalDevice(
320 const vector<const char*> validationLayers,
321 const vector<const char*>& deviceExtensions) {
322 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
323
324 vector<VkDeviceQueueCreateInfo> queueCreateInfos;
325 set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
326
327 float queuePriority = 1.0f;
328 for (uint32_t queueFamily : uniqueQueueFamilies) {
329 VkDeviceQueueCreateInfo queueCreateInfo = {};
330 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
331 queueCreateInfo.queueFamilyIndex = queueFamily;
332 queueCreateInfo.queueCount = 1;
333 queueCreateInfo.pQueuePriorities = &queuePriority;
334
335 queueCreateInfos.push_back(queueCreateInfo);
336 }
337
338 VkPhysicalDeviceFeatures deviceFeatures = {};
339 deviceFeatures.samplerAnisotropy = VK_TRUE;
340
341 VkDeviceCreateInfo createInfo = {};
342 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
343 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
344 createInfo.pQueueCreateInfos = queueCreateInfos.data();
345
346 createInfo.pEnabledFeatures = &deviceFeatures;
347
348 createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
349 createInfo.ppEnabledExtensionNames = deviceExtensions.data();
350
351 // These fields are ignored by up-to-date Vulkan implementations,
352 // but it's a good idea to set them for backwards compatibility
353 if (ENABLE_VALIDATION_LAYERS) {
354 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
355 createInfo.ppEnabledLayerNames = validationLayers.data();
356 } else {
357 createInfo.enabledLayerCount = 0;
358 }
359
360 if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
361 throw runtime_error("failed to create logical device!");
362 }
363
364 vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
365 vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
366}
367
368void VulkanGame::createSwapChain() {
369 SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
370
371 VkSurfaceFormatKHR surfaceFormat = VulkanUtils::chooseSwapSurfaceFormat(swapChainSupport.formats);
372 VkPresentModeKHR presentMode = VulkanUtils::chooseSwapPresentMode(swapChainSupport.presentModes);
373 VkExtent2D extent = VulkanUtils::chooseSwapExtent(swapChainSupport.capabilities, gui->getWindowWidth(), gui->getWindowHeight());
374
375 uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
376 if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) {
377 imageCount = swapChainSupport.capabilities.maxImageCount;
378 }
379
380 VkSwapchainCreateInfoKHR createInfo = {};
381 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
382 createInfo.surface = surface;
383 createInfo.minImageCount = imageCount;
384 createInfo.imageFormat = surfaceFormat.format;
385 createInfo.imageColorSpace = surfaceFormat.colorSpace;
386 createInfo.imageExtent = extent;
387 createInfo.imageArrayLayers = 1;
388 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
389
390 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
391 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
392
393 if (indices.graphicsFamily != indices.presentFamily) {
394 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
395 createInfo.queueFamilyIndexCount = 2;
396 createInfo.pQueueFamilyIndices = queueFamilyIndices;
397 } else {
398 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
399 createInfo.queueFamilyIndexCount = 0;
400 createInfo.pQueueFamilyIndices = nullptr;
401 }
402
403 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
404 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
405 createInfo.presentMode = presentMode;
406 createInfo.clipped = VK_TRUE;
407 createInfo.oldSwapchain = VK_NULL_HANDLE;
408
409 if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
410 throw runtime_error("failed to create swap chain!");
411 }
412
413 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
414 swapChainImages.resize(imageCount);
415 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
416
417 swapChainImageFormat = surfaceFormat.format;
418 swapChainExtent = extent;
419}
420
421void VulkanGame::createImageViews() {
422 swapChainImageViews.resize(swapChainImages.size());
423
424 for (size_t i = 0; i < swapChainImages.size(); i++) {
425 swapChainImageViews[i] = VulkanUtils::createImageView(device, swapChainImages[i], swapChainImageFormat,
426 VK_IMAGE_ASPECT_COLOR_BIT);
427 }
428}
429
430void VulkanGame::createRenderPass() {
431 VkAttachmentDescription colorAttachment = {};
432 colorAttachment.format = swapChainImageFormat;
433 colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
434 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
435 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
436 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
437 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
438 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
439 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
440
441 VkAttachmentReference colorAttachmentRef = {};
442 colorAttachmentRef.attachment = 0;
443 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
444
445 VkAttachmentDescription depthAttachment = {};
446 depthAttachment.format = findDepthFormat();
447 depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
448 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
449 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
450 depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
451 depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
452 depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
453 depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
454
455 VkAttachmentReference depthAttachmentRef = {};
456 depthAttachmentRef.attachment = 1;
457 depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
458
459 VkSubpassDescription subpass = {};
460 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
461 subpass.colorAttachmentCount = 1;
462 subpass.pColorAttachments = &colorAttachmentRef;
463 subpass.pDepthStencilAttachment = &depthAttachmentRef;
464
465 VkSubpassDependency dependency = {};
466 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
467 dependency.dstSubpass = 0;
468 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
469 dependency.srcAccessMask = 0;
470 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
471 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
472
473 array<VkAttachmentDescription, 2> attachments = { colorAttachment, depthAttachment };
474 VkRenderPassCreateInfo renderPassInfo = {};
475 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
476 renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
477 renderPassInfo.pAttachments = attachments.data();
478 renderPassInfo.subpassCount = 1;
479 renderPassInfo.pSubpasses = &subpass;
480 renderPassInfo.dependencyCount = 1;
481 renderPassInfo.pDependencies = &dependency;
482
483 if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
484 throw runtime_error("failed to create render pass!");
485 }
486}
487
488VkFormat VulkanGame::findDepthFormat() {
489 return VulkanUtils::findSupportedFormat(
490 physicalDevice,
491 { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
492 VK_IMAGE_TILING_OPTIMAL,
493 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
494 );
495}
496
497void VulkanGame::createCommandPool() {
498 QueueFamilyIndices queueFamilyIndices = VulkanUtils::findQueueFamilies(physicalDevice, surface);;
499
500 VkCommandPoolCreateInfo poolInfo = {};
501 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
502 poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
503 poolInfo.flags = 0;
504
505 if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
506 throw runtime_error("failed to create graphics command pool!");
507 }
508}
509
510void VulkanGame::cleanupSwapChain() {
511 vkDestroyRenderPass(device, renderPass, nullptr);
512
513 for (auto imageView : swapChainImageViews) {
514 vkDestroyImageView(device, imageView, nullptr);
515 }
516
517 vkDestroySwapchainKHR(device, swapChain, nullptr);
518}
Note: See TracBrowser for help on using the repository browser.