source: opengl-game/vulkan-game.cpp@ 321272c

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

Create an SDL surface and usse it to fill the window with a white background

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