source: opengl-game/vulkan-game.cpp@ 6fc24c7

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

In vulkangame, add code to create a render pass

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