source: opengl-game/vulkan-game.cpp@ 98f3232

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

Use GameGui as the base class for GameGui_SDL

  • Property mode set to 100644
File size: 14.1 KB
Line 
1#include <vulkan/vulkan.h>
2
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>
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>
14#include <vector>
15#include <set>
16#include <stdexcept>
17#include <cstdlib>
18#include <optional>
19
20#include "game-gui-sdl.hpp"
21
22using namespace std;
23using namespace glm;
24
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
38struct QueueFamilyIndices {
39 optional<uint32_t> graphicsFamily;
40 optional<uint32_t> presentFamily;
41
42 bool isComplete() {
43 return graphicsFamily.has_value() && presentFamily.has_value();
44 }
45};
46
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
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
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* gui = new GameGui_SDL();
94 SDL_Window* window = nullptr;
95
96 VkInstance instance;
97 VkDebugUtilsMessengerEXT debugMessenger;
98 VkSurfaceKHR surface;
99
100 VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
101 VkDevice device;
102
103 VkQueue graphicsQueue;
104 VkQueue presentQueue;
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
121 if (window == nullptr) {
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();
132 setupDebugMessenger();
133 createSurface();
134 pickPhysicalDevice();
135 createLogicalDevice();
136 }
137
138 void createInstance() {
139 if (enableValidationLayers && !checkValidationLayerSupport()) {
140 throw runtime_error("validation layers requested, but not available!");
141 }
142
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
155 vector<const char*> extensions = getRequiredExtensions();
156 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
157 createInfo.ppEnabledExtensionNames = extensions.data();
158
159 cout << endl << "SDL extensions:" << endl;
160 for (const char* extensionName : extensions) {
161 cout << extensionName << endl;
162 }
163 cout << endl;
164
165 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
166 if (enableValidationLayers) {
167 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
168 createInfo.ppEnabledLayerNames = validationLayers.data();
169
170 populateDebugMessengerCreateInfo(debugCreateInfo);
171 createInfo.pNext = &debugCreateInfo;
172 } else {
173 createInfo.enabledLayerCount = 0;
174
175 createInfo.pNext = nullptr;
176 }
177
178 if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
179 throw runtime_error("failed to create instance!");
180 }
181 }
182
183 void setupDebugMessenger() {
184 if (!enableValidationLayers) return;
185
186 VkDebugUtilsMessengerCreateInfoEXT createInfo;
187 populateDebugMessengerCreateInfo(createInfo);
188
189 if (CreateDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
190 throw runtime_error("failed to setup debug messenger!");
191 }
192 }
193
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
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
250 vector<VkDeviceQueueCreateInfo> queueCreateInfos;
251 set<uint32_t> uniqueQueueFamilies = {indices.graphicsFamily.value(), indices.presentFamily.value()};
252
253 float queuePriority = 1.0f;
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 }
264
265 VkPhysicalDeviceFeatures deviceFeatures = {};
266
267 VkDeviceCreateInfo createInfo = {};
268 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
269
270 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());;
271 createInfo.pQueueCreateInfos = queueCreateInfos.data();
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);
291 vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
292 }
293
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
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
334 VkBool32 presentSupport = false;
335 vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport);
336
337 if (queueFamily.queueCount > 0 && presentSupport) {
338 indices.presentFamily = i;
339 }
340
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
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
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;
382
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
390 while (!quit) {
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() {
406 vkDestroyDevice(device, nullptr);
407
408 if (enableValidationLayers) {
409 DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
410 }
411
412 vkDestroySurfaceKHR(instance, surface, nullptr);
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 delete gui;
420 }
421};
422
423int main(int argc, char* argv[]) {
424
425#ifdef NDEBUG
426 cout << "DEBUGGING IS OFF" << endl;
427#else
428 cout << "DEBUGGING IS ON" << endl;
429#endif
430
431 mat4 matrix;
432 vec4 vec;
433 vec4 test = matrix * vec;
434
435 cout << "Starting Vulkan game..." << endl;
436
437 VulkanGame game;
438
439 try {
440 game.run();
441 } catch (const exception& e) {
442 cerr << e.what() << endl;
443 return EXIT_FAILURE;
444 }
445
446 cout << "Finished running the game" << endl;
447
448 return EXIT_SUCCESS;
449}
Note: See TracBrowser for help on using the repository browser.