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

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

Create the VulkanGame VS2019 project and add it to the NewOpenGLGame solution

  • Property mode set to 100644
File size: 14.0 KB
RevLine 
[826df16]1#include <vulkan/vulkan.h>
[03f4c64]2
[826df16]3#include <SDL2/SDL.h>
4#include <SDL2/SDL_vulkan.h>
5
6//#define _USE_MATH_DEFINES // Will be needed when/if I need to # include <cmath>
[03f4c64]7
8#define GLM_FORCE_RADIANS
9#define GLM_FORCE_DEPTH_ZERO_TO_ONE
10#include <glm/vec4.hpp>
11#include <glm/mat4x4.hpp>
12
13#include <iostream>
[826df16]14#include <vector>
[b3671b5]15#include <set>
[826df16]16#include <stdexcept>
17#include <cstdlib>
[909b51a]18#include <optional>
[826df16]19
20#include "game-gui-sdl.hpp"
[03f4c64]21
22using namespace std;
23using namespace glm;
24
[826df16]25const int SCREEN_WIDTH = 800;
26const int SCREEN_HEIGHT = 600;
27
28const vector<const char*> validationLayers = {
29 "VK_LAYER_KHRONOS_validation"
30};
31
32#ifdef NDEBUG
33 const bool enableValidationLayers = false;
34#else
35 const bool enableValidationLayers = true;
36#endif
37
[909b51a]38struct QueueFamilyIndices {
39 optional<uint32_t> graphicsFamily;
[b3671b5]40 optional<uint32_t> presentFamily;
[909b51a]41
42 bool isComplete() {
[b3671b5]43 return graphicsFamily.has_value() && presentFamily.has_value();
[909b51a]44 }
45};
46
[b6127d2]47static VKAPI_ATTR VkBool32 VKAPI_CALL debugCallback(
48 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
49 VkDebugUtilsMessageTypeFlagsEXT messageType,
50 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
51 void* pUserData) {
52 cerr << "validation layer: " << pCallbackData->pMessage << endl;
53
54 return VK_FALSE;
55}
56
57VkResult CreateDebugUtilsMessengerEXT(VkInstance instance,
58 const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
59 const VkAllocationCallbacks* pAllocator,
60 VkDebugUtilsMessengerEXT* pDebugMessenger) {
61 auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(
62 instance, "vkCreateDebugUtilsMessengerEXT");
63
64 if (func != nullptr) {
65 return func(instance, pCreateInfo, pAllocator, pDebugMessenger);
66 } else {
67 return VK_ERROR_EXTENSION_NOT_PRESENT;
68 }
69}
70
[80de39d]71void DestroyDebugUtilsMessengerEXT(VkInstance instance,
72 VkDebugUtilsMessengerEXT debugMessenger,
73 const VkAllocationCallbacks* pAllocator) {
74 auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(
75 instance, "vkDestroyDebugUtilsMessengerEXT");
76
77 if (func != nullptr) {
78 func(instance, debugMessenger, pAllocator);
79 }
80}
81
[826df16]82class VulkanGame {
83 public:
84 void run() {
85 if (initWindow() == RTWO_ERROR) {
86 return;
87 }
88 initVulkan();
89 mainLoop();
90 cleanup();
91 }
92 private:
93 GameGui_SDL gui;
[80de39d]94 SDL_Window* window = nullptr;
[826df16]95
96 VkInstance instance;
[b6127d2]97 VkDebugUtilsMessengerEXT debugMessenger;
[b3671b5]98 VkSurfaceKHR surface;
99
[909b51a]100 VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
101 VkDevice device;
[b3671b5]102
[909b51a]103 VkQueue graphicsQueue;
[b3671b5]104 VkQueue presentQueue;
[826df16]105
106 // both SDL and GLFW create window functions return NULL on failure
107 bool initWindow() {
108 if (gui.Init() == RTWO_ERROR) {
109 cout << "UI library could not be initialized!" << endl;
110 return RTWO_ERROR;
111 } else {
112 // On Apple's OS X you must set the NSHighResolutionCapable Info.plist property to YES,
113 // otherwise you will not receive a High DPI OpenGL canvas.
114
115 // TODO: Move this into some generic method in game-gui-sdl
116 window = SDL_CreateWindow("Vulkan Game",
117 SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
118 SCREEN_WIDTH, SCREEN_HEIGHT,
119 SDL_WINDOW_VULKAN | SDL_WINDOW_SHOWN);
120
[80de39d]121 if (window == nullptr) {
[826df16]122 cout << "Window could not be created!" << endl;
123 return RTWO_ERROR;
124 } else {
125 return RTWO_SUCCESS;
126 }
127 }
128 }
129
130 void initVulkan() {
131 createInstance();
[7dcd925]132 setupDebugMessenger();
[b3671b5]133 createSurface();
[909b51a]134 pickPhysicalDevice();
135 createLogicalDevice();
[826df16]136 }
137
138 void createInstance() {
[b6127d2]139 if (enableValidationLayers && !checkValidationLayerSupport()) {
140 throw runtime_error("validation layers requested, but not available!");
141 }
142
[826df16]143 VkApplicationInfo appInfo = {};
144 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
145 appInfo.pApplicationName = "Vulkan Game";
146 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
147 appInfo.pEngineName = "No Engine";
148 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
149 appInfo.apiVersion = VK_API_VERSION_1_0;
150
151 VkInstanceCreateInfo createInfo = {};
152 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
153 createInfo.pApplicationInfo = &appInfo;
154
[a8f0577]155 vector<const char*> extensions = getRequiredExtensions();
[b6127d2]156 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
157 createInfo.ppEnabledExtensionNames = extensions.data();
[826df16]158
[b3671b5]159 cout << endl << "SDL extensions:" << endl;
160 for (const char* extensionName : extensions) {
161 cout << extensionName << endl;
162 }
163 cout << endl;
164
[80de39d]165 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
[b6127d2]166 if (enableValidationLayers) {
167 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
168 createInfo.ppEnabledLayerNames = validationLayers.data();
[80de39d]169
170 populateDebugMessengerCreateInfo(debugCreateInfo);
171 createInfo.pNext = &debugCreateInfo;
[b6127d2]172 } else {
173 createInfo.enabledLayerCount = 0;
[80de39d]174
175 createInfo.pNext = nullptr;
[b6127d2]176 }
[826df16]177
178 if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
179 throw runtime_error("failed to create instance!");
180 }
181 }
182
[80de39d]183 void setupDebugMessenger() {
184 if (!enableValidationLayers) return;
185
186 VkDebugUtilsMessengerCreateInfoEXT createInfo;
187 populateDebugMessengerCreateInfo(createInfo);
[b6127d2]188
189 if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
190 throw runtime_error("failed to setup debug messenger!");
191 }
192 }
193
[b3671b5]194 void createSurface() {
195 //SDL_Surface* screenSurface = nullptr;
196
197 if (!SDL_Vulkan_CreateSurface(window, instance, &surface)) {
198 throw runtime_error("failed to create window surface!");
199 }
200
201 /*
202 if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) {
203 throw runtime_error("failed to create window surface!");
204 }
205 */
206 }
207
[909b51a]208 void pickPhysicalDevice() {
209 uint32_t deviceCount = 0;
210 vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
211
212 if (deviceCount == 0) {
213 throw runtime_error("failed to find GPUs with Vulkan support!");
214 }
215
216 vector<VkPhysicalDevice> devices(deviceCount);
217 vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
218
219 cout << endl << "Graphics cards:" << endl;
220 for (const VkPhysicalDevice& device : devices) {
221 if (isDeviceSuitable(device)) {
222 physicalDevice = device;
223 break;
224 }
225 }
226 cout << endl;
227
228 if (physicalDevice == VK_NULL_HANDLE) {
229 throw runtime_error("failed to find a suitable GPU!");
230 }
231 }
232
233 bool isDeviceSuitable(VkPhysicalDevice device) {
234 VkPhysicalDeviceProperties deviceProperties;
235 VkPhysicalDeviceFeatures deviceFeatures;
236
237 vkGetPhysicalDeviceProperties(device, &deviceProperties);
238 vkGetPhysicalDeviceFeatures(device, &deviceFeatures);
239
240 cout << "Device: " << deviceProperties.deviceName << endl;
241
242 QueueFamilyIndices indices = findQueueFamilies(device);
243
244 return indices.isComplete();
245 }
246
247 void createLogicalDevice() {
248 QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
249
[b3671b5]250 vector<VkDeviceQueueCreateInfo> queueCreateInfos;
251 set<uint32_t> uniqueQueueFamilies = {indices.graphicsFamily.value(), indices.presentFamily.value()};
[909b51a]252
253 float queuePriority = 1.0f;
[b3671b5]254 for (uint32_t queueFamily : uniqueQueueFamilies) {
255 VkDeviceQueueCreateInfo queueCreateInfo = {};
256
257 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
258 queueCreateInfo.queueFamilyIndex = queueFamily;
259 queueCreateInfo.queueCount = 1;
260 queueCreateInfo.pQueuePriorities = &queuePriority;
261
262 queueCreateInfos.push_back(queueCreateInfo);
263 }
[909b51a]264
265 VkPhysicalDeviceFeatures deviceFeatures = {};
266
267 VkDeviceCreateInfo createInfo = {};
268 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
269
[b3671b5]270 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());;
271 createInfo.pQueueCreateInfos = queueCreateInfos.data();
[909b51a]272
273 createInfo.pEnabledFeatures = &deviceFeatures;
274
275 createInfo.enabledExtensionCount = 0;
276
277 // These fields are ignored by up-to-date Vulkan implementations,
278 // but it's a good idea to set them for backwards compatibility
279 if (enableValidationLayers) {
280 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
281 createInfo.ppEnabledLayerNames = validationLayers.data();
282 } else {
283 createInfo.enabledLayerCount = 0;
284 }
285
286 if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
287 throw runtime_error("failed to create logical device!");
288 }
289
290 vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
[b3671b5]291 vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
[909b51a]292 }
293
[a8f0577]294 bool checkValidationLayerSupport() {
295 uint32_t layerCount;
296 vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
297
298 vector<VkLayerProperties> availableLayers(layerCount);
299 vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data());
300
301 for (const char* layerName : validationLayers) {
302 bool layerFound = false;
303
304 for (const auto& layerProperties : availableLayers) {
305 if (strcmp(layerName, layerProperties.layerName) == 0) {
306 layerFound = true;
307 break;
308 }
309 }
310
311 if (!layerFound) {
312 return false;
313 }
314 }
315
316 return true;
317 }
318
[909b51a]319 QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
320 QueueFamilyIndices indices;
321
322 uint32_t queueFamilyCount = 0;
323 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr);
324
325 vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
326 vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data());
327
328 int i = 0;
329 for (const auto& queueFamily : queueFamilies) {
330 if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
331 indices.graphicsFamily = i;
332 }
333
[b3671b5]334 VkBool32 presentSupport = false;
335 vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
336
337 if (queueFamily.queueCount > 0 && presentSupport) {
338 indices.presentFamily = i;
339 }
340
[909b51a]341 if (indices.isComplete()) {
342 break;
343 }
344
345 i++;
346 }
347
348 return indices;
349 }
350
351 void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
352 createInfo = {};
353 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
354 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;
355 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;
356 createInfo.pfnUserCallback = debugCallback;
357 }
358
[a8f0577]359 vector<const char*> getRequiredExtensions() {
360 uint32_t extensionCount = 0;
361 SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, nullptr);
362
363 vector<const char*> extensions(extensionCount);
364 SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, extensions.data());
365
366 if (enableValidationLayers) {
367 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
368 }
369
370 return extensions;
371 }
372
[826df16]373 void mainLoop() {
374 // TODO: Create some generic event-handling functions in game-gui-*
375 SDL_Event e;
376 bool quit = false;
377
378 /*
379 screenSurface = SDL_GetWindowSurface(window);
380 cout << "Got here" << endl;
381 cout << (screenSurface == nullptr ? "true" : "false") << endl;
[03f4c64]382
[826df16]383 SDL_FillRect(screenSurface, nullptr, SDL_MapRGB(screenSurface->format, 0xFF, 0xFF, 0xFF));
384 cout << "Filled" << endl;
385
386 SDL_UpdateWindowSurface(window);
387 cout << "Updated" << endl;
388 */
389
[7dcd925]390 while (!quit) {
[826df16]391 while (SDL_PollEvent(&e)) {
392 if (e.type == SDL_QUIT) {
393 quit = true;
394 }
395 if (e.type == SDL_KEYDOWN) {
396 quit = true;
397 }
398 if (e.type == SDL_MOUSEBUTTONDOWN) {
399 quit = true;
400 }
401 }
402 }
403 }
404
405 void cleanup() {
[909b51a]406 vkDestroyDevice(device, nullptr);
407
[80de39d]408 if (enableValidationLayers) {
409 DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
410 }
411
[b3671b5]412 vkDestroySurfaceKHR(instance, surface, nullptr);
[826df16]413 vkDestroyInstance(instance, nullptr);
414
415 // TODO: Move this into some generic method in game-gui-sdl
416 SDL_DestroyWindow(window);
417
418 gui.Shutdown();
419 }
420};
421
[1c6cd5e]422int main(int argc, char* argv[]) {
[826df16]423
[b6127d2]424#ifdef NDEBUG
425 cout << "DEBUGGING IS OFF" << endl;
426#else
427 cout << "DEBUGGING IS ON" << endl;
428#endif
[a8f0577]429
[826df16]430 mat4 matrix;
431 vec4 vec;
432 vec4 test = matrix * vec;
433
434 cout << "Starting Vulkan game..." << endl;
435
436 VulkanGame game;
437
438 try {
439 game.run();
440 } catch (const exception& e) {
441 cerr << e.what() << endl;
442 return EXIT_FAILURE;
443 }
[03f4c64]444
[826df16]445 cout << "Finished running the game" << endl;
[03f4c64]446
[826df16]447 return EXIT_SUCCESS;
[03f4c64]448}
Note: See TracBrowser for help on using the repository browser.