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

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

In vulkangame, add code to create image views

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