source: opengl-game/vulkan-game.cpp@ 87c8f1a

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

In vaulkangame, define vertex buffer and index buffer data and transfer it to the gpu

  • Property mode set to 100644
File size: 31.3 KB
RevLine 
[99d44b2]1#include "vulkan-game.hpp"
[850e84c]2
[6fc24c7]3#include <array>
[0df3c9a]4#include <iostream>
[c1c2021]5#include <set>
[0df3c9a]6
[5edbd58]7#include "consts.hpp"
[c559904]8#include "logger.hpp"
[5edbd58]9
[771b33a]10#include "utils.hpp"
[c1d9b2a]11
[0df3c9a]12using namespace std;
13
[b794178]14struct UniformBufferObject {
15 alignas(16) mat4 model;
16 alignas(16) mat4 view;
17 alignas(16) mat4 proj;
18};
19
[34bdf3a]20VulkanGame::VulkanGame(int maxFramesInFlight) : MAX_FRAMES_IN_FLIGHT(maxFramesInFlight) {
[0df3c9a]21 gui = nullptr;
22 window = nullptr;
[87c8f1a]23
24 currentFrame = 0;
25 framebufferResized = false;
[0df3c9a]26}
27
[99d44b2]28VulkanGame::~VulkanGame() {
[0df3c9a]29}
30
[b6e60b4]31void VulkanGame::run(int width, int height, unsigned char guiFlags) {
[2e77b3f]32 cout << "DEBUGGING IS " << (ENABLE_VALIDATION_LAYERS ? "ON" : "OFF") << endl;
33
34 cout << "Vulkan Game" << endl;
35
[c559904]36 // This gets the runtime version, use SDL_VERSION() for the comppile-time version
37 // TODO: Create a game-gui function to get the gui version and retrieve it that way
38 SDL_GetVersion(&sdlVersion);
39
40 // TODO: Refactor the logger api to be more flexible,
41 // esp. since gl_log() and gl_log_err() have issues printing anything besides stirngs
42 restart_gl_log();
43 gl_log("starting SDL\n%s.%s.%s",
44 to_string(sdlVersion.major).c_str(),
45 to_string(sdlVersion.minor).c_str(),
46 to_string(sdlVersion.patch).c_str());
47
48 open_log();
49 get_log() << "starting SDL" << endl;
50 get_log() <<
51 (int)sdlVersion.major << "." <<
52 (int)sdlVersion.minor << "." <<
53 (int)sdlVersion.patch << endl;
54
[5edbd58]55 if (initWindow(width, height, guiFlags) == RTWO_ERROR) {
[0df3c9a]56 return;
57 }
[b6e60b4]58
[0df3c9a]59 initVulkan();
60 mainLoop();
61 cleanup();
[c559904]62
63 close_log();
[0df3c9a]64}
65
[c559904]66// TODO: Make some more initi functions, or call this initUI if the
67// amount of things initialized here keeps growing
[b6e60b4]68bool VulkanGame::initWindow(int width, int height, unsigned char guiFlags) {
[c559904]69 // TODO: Put all fonts, textures, and images in the assets folder
[0df3c9a]70 gui = new GameGui_SDL();
71
[b6e60b4]72 if (gui->init() == RTWO_ERROR) {
[c559904]73 // TODO: Also print these sorts of errors to the log
[0df3c9a]74 cout << "UI library could not be initialized!" << endl;
[b6e60b4]75 cout << gui->getError() << endl;
[0df3c9a]76 return RTWO_ERROR;
77 }
78
[b6e60b4]79 window = (SDL_Window*) gui->createWindow("Vulkan Game", width, height, guiFlags & GUI_FLAGS_WINDOW_FULLSCREEN);
[0df3c9a]80 if (window == nullptr) {
81 cout << "Window could not be created!" << endl;
[ed7c953]82 cout << gui->getError() << endl;
[0df3c9a]83 return RTWO_ERROR;
84 }
85
[b6e60b4]86 cout << "Target window size: (" << width << ", " << height << ")" << endl;
[a6f6833]87 cout << "Actual window size: (" << gui->getWindowWidth() << ", " << gui->getWindowHeight() << ")" << endl;
[b6e60b4]88
[c1d9b2a]89 renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
90 if (renderer == nullptr) {
91 cout << "Renderer could not be created!" << endl;
92 cout << gui->getError() << endl;
93 return RTWO_ERROR;
94 }
95
[b794178]96 SDL_VERSION(&sdlVersion);
97
98 // In SDL 2.0.10 (currently, the latest), SDL_TEXTUREACCESS_TARGET is required to get a transparent overlay working
99 // However, the latest SDL version available through homebrew on Mac is 2.0.9, which requires SDL_TEXTUREACCESS_STREAMING
100 // I tried building sdl 2.0.10 (and sdl_image and sdl_ttf) from source on Mac, but had some issues, so this is easier
101 // until the homebrew recipe is updated
102 if (sdlVersion.major == 2 && sdlVersion.minor == 0 && sdlVersion.patch == 9) {
103 uiOverlay = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING,
104 gui->getWindowWidth(), gui->getWindowHeight());
105 } else {
106 uiOverlay = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,
107 gui->getWindowWidth(), gui->getWindowHeight());
108 }
109
110 if (uiOverlay == nullptr) {
111 cout << "Unable to create blank texture! SDL Error: " << SDL_GetError() << endl;
112 }
113 if (SDL_SetTextureBlendMode(uiOverlay, SDL_BLENDMODE_BLEND) != 0) {
114 cout << "Unable to set texture blend mode! SDL Error: " << SDL_GetError() << endl;
115 }
116
[0df3c9a]117 return RTWO_SUCCESS;
118}
119
[99d44b2]120void VulkanGame::initVulkan() {
[c1d9b2a]121 const vector<const char*> validationLayers = {
122 "VK_LAYER_KHRONOS_validation"
123 };
[fe5c3ba]124 const vector<const char*> deviceExtensions = {
125 VK_KHR_SWAPCHAIN_EXTENSION_NAME
126 };
[c1d9b2a]127
128 createVulkanInstance(validationLayers);
129 setupDebugMessenger();
[90a424f]130 createVulkanSurface();
[fe5c3ba]131 pickPhysicalDevice(deviceExtensions);
[c1c2021]132 createLogicalDevice(validationLayers, deviceExtensions);
[502bd0b]133 createSwapChain();
[f94eea9]134 createImageViews();
[6fc24c7]135 createRenderPass();
[fa9fa1c]136 createCommandPool();
[7d2b0b9]137
[603b5bc]138 createImageResources();
[b794178]139
[603b5bc]140 createFramebuffers();
141 createUniformBuffers();
142
[87c8f1a]143 vector<Vertex> sceneVertices = {
144 {{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
145 {{ 0.5f, -0.5f, -0.5f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
146 {{ 0.5f, 0.5f, -0.5f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
147 {{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}},
148
149 {{-0.5f, -0.5f, 0.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 1.0f}},
150 {{ 0.5f, -0.5f, 0.0f}, {0.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
151 {{ 0.5f, 0.5f, 0.0f}, {0.0f, 0.0f, 1.0f}, {1.0f, 0.0f}},
152 {{-0.5f, 0.5f, 0.0f}, {1.0f, 1.0f, 1.0f}, {0.0f, 0.0f}}
153 };
154 vector<uint16_t> sceneIndices = {
155 0, 1, 2, 2, 3, 0,
156 4, 5, 6, 6, 7, 4
157 };
158
159 graphicsPipelines.push_back(GraphicsPipeline_Vulkan(physicalDevice, device, renderPass,
[603b5bc]160 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, sizeof(Vertex)));
[771b33a]161
[87c8f1a]162 graphicsPipelines.back().bindData(sceneVertices, sceneIndices, commandPool, graphicsQueue);
163
[771b33a]164 graphicsPipelines.back().addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&Vertex::pos));
165 graphicsPipelines.back().addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&Vertex::color));
166 graphicsPipelines.back().addAttribute(VK_FORMAT_R32G32_SFLOAT, offset_of(&Vertex::texCoord));
167
[b794178]168 graphicsPipelines.back().addDescriptorInfo(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
169 VK_SHADER_STAGE_VERTEX_BIT, &uniformBufferInfoList);
170 graphicsPipelines.back().addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
171 VK_SHADER_STAGE_FRAGMENT_BIT, &floorTextureImageDescriptor);
172
173 graphicsPipelines.back().createDescriptorSetLayout();
[7d2b0b9]174 graphicsPipelines.back().createPipeline("shaders/scene-vert.spv", "shaders/scene-frag.spv");
[b794178]175 graphicsPipelines.back().createDescriptorPool(swapChainImages);
176 graphicsPipelines.back().createDescriptorSets(swapChainImages);
[7d2b0b9]177
[87c8f1a]178 vector<OverlayVertex> overlayVertices = {
179 {{-1.0f, 1.0f, 0.0f}, {0.0f, 1.0f}},
180 {{ 1.0f, 1.0f, 0.0f}, {1.0f, 1.0f}},
181 {{ 1.0f, -1.0f, 0.0f}, {1.0f, 0.0f}},
182 {{-1.0f, -1.0f, 0.0f}, {0.0f, 0.0f}}
183 };
184 vector<uint16_t> overlayIndices = {
185 0, 1, 2, 2, 3, 0
186 };
187
188 graphicsPipelines.push_back(GraphicsPipeline_Vulkan(physicalDevice, device, renderPass,
[603b5bc]189 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, sizeof(OverlayVertex)));
[771b33a]190
[87c8f1a]191 graphicsPipelines.back().bindData(overlayVertices, overlayIndices, commandPool, graphicsQueue);
192
[771b33a]193 graphicsPipelines.back().addAttribute(VK_FORMAT_R32G32B32_SFLOAT, offset_of(&OverlayVertex::pos));
194 graphicsPipelines.back().addAttribute(VK_FORMAT_R32G32_SFLOAT, offset_of(&OverlayVertex::texCoord));
195
[b794178]196 graphicsPipelines.back().addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
197 VK_SHADER_STAGE_FRAGMENT_BIT, &sdlOverlayImageDescriptor);
198
199 graphicsPipelines.back().createDescriptorSetLayout();
[7d2b0b9]200 graphicsPipelines.back().createPipeline("shaders/overlay-vert.spv", "shaders/overlay-frag.spv");
[b794178]201 graphicsPipelines.back().createDescriptorPool(swapChainImages);
202 graphicsPipelines.back().createDescriptorSets(swapChainImages);
[7d2b0b9]203
[603b5bc]204 // TODO: Creating the descriptor pool and descriptor sets might need to be redone when the
205 // swap chain is recreated
206
[7d2b0b9]207 cout << "Created " << graphicsPipelines.size() << " graphics pipelines" << endl;
[34bdf3a]208
209 createCommandBuffers();
210
211 createSyncObjects();
[0df3c9a]212}
213
[99d44b2]214void VulkanGame::mainLoop() {
[f6521fb]215 UIEvent e;
[0df3c9a]216 bool quit = false;
217
[c1d9b2a]218 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
219
[0df3c9a]220 while (!quit) {
[27c40ce]221 gui->processEvents();
222
[f6521fb]223 while (gui->pollEvent(&e)) {
224 switch(e.type) {
225 case UI_EVENT_QUIT:
226 cout << "Quit event detected" << endl;
227 quit = true;
228 break;
229 case UI_EVENT_WINDOW:
230 cout << "Window event detected" << endl;
231 // Currently unused
232 break;
[0e09340]233 case UI_EVENT_WINDOWRESIZE:
234 cout << "Window resize event detected" << endl;
235 framebufferResized = true;
236 break;
[f6521fb]237 case UI_EVENT_KEY:
238 if (e.key.keycode == SDL_SCANCODE_ESCAPE) {
239 quit = true;
240 } else {
241 cout << "Key event detected" << endl;
242 }
243 break;
244 case UI_EVENT_MOUSEBUTTONDOWN:
245 cout << "Mouse button down event detected" << endl;
246 break;
247 case UI_EVENT_MOUSEBUTTONUP:
248 cout << "Mouse button up event detected" << endl;
249 break;
250 case UI_EVENT_MOUSEMOTION:
251 break;
[a0da009]252 case UI_EVENT_UNKNOWN:
253 cout << "Unknown event type: 0x" << hex << e.unknown.eventType << dec << endl;
254 break;
[c61323a]255 default:
256 cout << "Unhandled UI event: " << e.type << endl;
[0df3c9a]257 }
258 }
[c1d9b2a]259
[a0c5f28]260 renderUI();
261 renderScene();
[0df3c9a]262 }
[c1c2021]263
264 vkDeviceWaitIdle(device);
[0df3c9a]265}
266
[a0c5f28]267void VulkanGame::renderUI() {
268 SDL_RenderClear(renderer);
269 SDL_RenderPresent(renderer);
270}
271
272void VulkanGame::renderScene() {
[87c8f1a]273 vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, numeric_limits<uint64_t>::max());
274
275 uint32_t imageIndex;
276
277 currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
278 currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
[a0c5f28]279}
280
[99d44b2]281void VulkanGame::cleanup() {
[c1c2021]282 cleanupSwapChain();
283
[e83b155]284 VulkanUtils::destroyVulkanImage(device, floorTextureImage);
285 VulkanUtils::destroyVulkanImage(device, sdlOverlayImage);
286
287 vkDestroySampler(device, textureSampler, nullptr);
288
[b794178]289 for (GraphicsPipeline_Vulkan pipeline : graphicsPipelines) {
290 pipeline.cleanupBuffers();
291 }
292
[34bdf3a]293 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
294 vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
295 vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
296 vkDestroyFence(device, inFlightFences[i], nullptr);
297 }
298
[fa9fa1c]299 vkDestroyCommandPool(device, commandPool, nullptr);
[c1c2021]300 vkDestroyDevice(device, nullptr);
301 vkDestroySurfaceKHR(instance, surface, nullptr);
302
[c1d9b2a]303 if (ENABLE_VALIDATION_LAYERS) {
304 VulkanUtils::destroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
305 }
[c1c2021]306
[c1d9b2a]307 vkDestroyInstance(instance, nullptr);
308
[b794178]309 // TODO: Check if any of these functions accept null parameters
310 // If they do, I don't need to check for that
311
312 if (uiOverlay != nullptr) {
313 SDL_DestroyTexture(uiOverlay);
314 uiOverlay = nullptr;
315 }
316
[c1d9b2a]317 SDL_DestroyRenderer(renderer);
318 renderer = nullptr;
319
[b6e60b4]320 gui->destroyWindow();
321 gui->shutdown();
[0df3c9a]322 delete gui;
[c1d9b2a]323}
324
325void VulkanGame::createVulkanInstance(const vector<const char*> &validationLayers) {
326 if (ENABLE_VALIDATION_LAYERS && !VulkanUtils::checkValidationLayerSupport(validationLayers)) {
327 throw runtime_error("validation layers requested, but not available!");
328 }
329
330 VkApplicationInfo appInfo = {};
331 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
332 appInfo.pApplicationName = "Vulkan Game";
333 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
334 appInfo.pEngineName = "No Engine";
335 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
336 appInfo.apiVersion = VK_API_VERSION_1_0;
337
338 VkInstanceCreateInfo createInfo = {};
339 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
340 createInfo.pApplicationInfo = &appInfo;
341
342 vector<const char*> extensions = gui->getRequiredExtensions();
343 if (ENABLE_VALIDATION_LAYERS) {
344 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
345 }
346
347 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
348 createInfo.ppEnabledExtensionNames = extensions.data();
349
350 cout << endl << "Extensions:" << endl;
351 for (const char* extensionName : extensions) {
352 cout << extensionName << endl;
353 }
354 cout << endl;
355
356 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
357 if (ENABLE_VALIDATION_LAYERS) {
358 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
359 createInfo.ppEnabledLayerNames = validationLayers.data();
360
361 populateDebugMessengerCreateInfo(debugCreateInfo);
362 createInfo.pNext = &debugCreateInfo;
363 } else {
364 createInfo.enabledLayerCount = 0;
365
366 createInfo.pNext = nullptr;
367 }
368
369 if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
370 throw runtime_error("failed to create instance!");
371 }
372}
373
374void VulkanGame::setupDebugMessenger() {
375 if (!ENABLE_VALIDATION_LAYERS) return;
376
377 VkDebugUtilsMessengerCreateInfoEXT createInfo;
378 populateDebugMessengerCreateInfo(createInfo);
379
380 if (VulkanUtils::createDebugUtilsMessengerEXT(instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
381 throw runtime_error("failed to set up debug messenger!");
382 }
383}
384
385void VulkanGame::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
386 createInfo = {};
387 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
388 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;
389 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;
390 createInfo.pfnUserCallback = debugCallback;
391}
392
393VKAPI_ATTR VkBool32 VKAPI_CALL VulkanGame::debugCallback(
394 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
395 VkDebugUtilsMessageTypeFlagsEXT messageType,
396 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
397 void* pUserData) {
398 cerr << "validation layer: " << pCallbackData->pMessage << endl;
399
400 return VK_FALSE;
401}
[90a424f]402
403void VulkanGame::createVulkanSurface() {
404 if (gui->createVulkanSurface(instance, &surface) == RTWO_ERROR) {
405 throw runtime_error("failed to create window surface!");
406 }
407}
408
[fe5c3ba]409void VulkanGame::pickPhysicalDevice(const vector<const char*>& deviceExtensions) {
[90a424f]410 uint32_t deviceCount = 0;
411 vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr);
412
413 if (deviceCount == 0) {
414 throw runtime_error("failed to find GPUs with Vulkan support!");
415 }
416
417 vector<VkPhysicalDevice> devices(deviceCount);
418 vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data());
419
420 cout << endl << "Graphics cards:" << endl;
421 for (const VkPhysicalDevice& device : devices) {
[fe5c3ba]422 if (isDeviceSuitable(device, deviceExtensions)) {
[90a424f]423 physicalDevice = device;
424 break;
425 }
426 }
427 cout << endl;
428
429 if (physicalDevice == VK_NULL_HANDLE) {
430 throw runtime_error("failed to find a suitable GPU!");
431 }
432}
433
[fa9fa1c]434bool VulkanGame::isDeviceSuitable(VkPhysicalDevice physicalDevice,
435 const vector<const char*>& deviceExtensions) {
[90a424f]436 VkPhysicalDeviceProperties deviceProperties;
[fa9fa1c]437 vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
[90a424f]438
439 cout << "Device: " << deviceProperties.deviceName << endl;
440
[fa9fa1c]441 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
442 bool extensionsSupported = VulkanUtils::checkDeviceExtensionSupport(physicalDevice, deviceExtensions);
[90a424f]443 bool swapChainAdequate = false;
444
445 if (extensionsSupported) {
[fa9fa1c]446 SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
[90a424f]447 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
448 }
449
450 VkPhysicalDeviceFeatures supportedFeatures;
[fa9fa1c]451 vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures);
[90a424f]452
453 return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
[c1c2021]454}
455
456void VulkanGame::createLogicalDevice(
457 const vector<const char*> validationLayers,
458 const vector<const char*>& deviceExtensions) {
459 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
460
[b794178]461 vector<VkDeviceQueueCreateInfo> queueCreateInfoList;
[c1c2021]462 set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
463
464 float queuePriority = 1.0f;
465 for (uint32_t queueFamily : uniqueQueueFamilies) {
466 VkDeviceQueueCreateInfo queueCreateInfo = {};
467 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
468 queueCreateInfo.queueFamilyIndex = queueFamily;
469 queueCreateInfo.queueCount = 1;
470 queueCreateInfo.pQueuePriorities = &queuePriority;
471
[b794178]472 queueCreateInfoList.push_back(queueCreateInfo);
[c1c2021]473 }
474
475 VkPhysicalDeviceFeatures deviceFeatures = {};
476 deviceFeatures.samplerAnisotropy = VK_TRUE;
477
478 VkDeviceCreateInfo createInfo = {};
479 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
[b794178]480 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfoList.size());
481 createInfo.pQueueCreateInfos = queueCreateInfoList.data();
[c1c2021]482
483 createInfo.pEnabledFeatures = &deviceFeatures;
484
485 createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
486 createInfo.ppEnabledExtensionNames = deviceExtensions.data();
487
488 // These fields are ignored by up-to-date Vulkan implementations,
489 // but it's a good idea to set them for backwards compatibility
490 if (ENABLE_VALIDATION_LAYERS) {
491 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
492 createInfo.ppEnabledLayerNames = validationLayers.data();
493 } else {
494 createInfo.enabledLayerCount = 0;
495 }
496
497 if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
498 throw runtime_error("failed to create logical device!");
499 }
500
501 vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
502 vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
[502bd0b]503}
504
505void VulkanGame::createSwapChain() {
506 SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
507
508 VkSurfaceFormatKHR surfaceFormat = VulkanUtils::chooseSwapSurfaceFormat(swapChainSupport.formats);
509 VkPresentModeKHR presentMode = VulkanUtils::chooseSwapPresentMode(swapChainSupport.presentModes);
510 VkExtent2D extent = VulkanUtils::chooseSwapExtent(swapChainSupport.capabilities, gui->getWindowWidth(), gui->getWindowHeight());
511
512 uint32_t imageCount = swapChainSupport.capabilities.minImageCount + 1;
513 if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount) {
514 imageCount = swapChainSupport.capabilities.maxImageCount;
515 }
516
517 VkSwapchainCreateInfoKHR createInfo = {};
518 createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
519 createInfo.surface = surface;
520 createInfo.minImageCount = imageCount;
521 createInfo.imageFormat = surfaceFormat.format;
522 createInfo.imageColorSpace = surfaceFormat.colorSpace;
523 createInfo.imageExtent = extent;
524 createInfo.imageArrayLayers = 1;
525 createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
526
527 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
528 uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
529
530 if (indices.graphicsFamily != indices.presentFamily) {
531 createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
532 createInfo.queueFamilyIndexCount = 2;
533 createInfo.pQueueFamilyIndices = queueFamilyIndices;
[f94eea9]534 } else {
[502bd0b]535 createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
536 createInfo.queueFamilyIndexCount = 0;
537 createInfo.pQueueFamilyIndices = nullptr;
538 }
539
540 createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
541 createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
542 createInfo.presentMode = presentMode;
543 createInfo.clipped = VK_TRUE;
544 createInfo.oldSwapchain = VK_NULL_HANDLE;
545
546 if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
547 throw runtime_error("failed to create swap chain!");
548 }
549
550 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
551 swapChainImages.resize(imageCount);
552 vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
553
554 swapChainImageFormat = surfaceFormat.format;
[603b5bc]555 swapChainExtent = extent;
[f94eea9]556}
557
558void VulkanGame::createImageViews() {
559 swapChainImageViews.resize(swapChainImages.size());
560
561 for (size_t i = 0; i < swapChainImages.size(); i++) {
562 swapChainImageViews[i] = VulkanUtils::createImageView(device, swapChainImages[i], swapChainImageFormat,
563 VK_IMAGE_ASPECT_COLOR_BIT);
564 }
565}
566
[6fc24c7]567void VulkanGame::createRenderPass() {
568 VkAttachmentDescription colorAttachment = {};
569 colorAttachment.format = swapChainImageFormat;
570 colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
571 colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
572 colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
573 colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
574 colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
575 colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
576 colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
577
578 VkAttachmentReference colorAttachmentRef = {};
579 colorAttachmentRef.attachment = 0;
580 colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
581
582 VkAttachmentDescription depthAttachment = {};
583 depthAttachment.format = findDepthFormat();
584 depthAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
585 depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
586 depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
587 depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
588 depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
589 depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
590 depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
591
592 VkAttachmentReference depthAttachmentRef = {};
593 depthAttachmentRef.attachment = 1;
594 depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
595
596 VkSubpassDescription subpass = {};
597 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
598 subpass.colorAttachmentCount = 1;
599 subpass.pColorAttachments = &colorAttachmentRef;
600 subpass.pDepthStencilAttachment = &depthAttachmentRef;
601
602 VkSubpassDependency dependency = {};
603 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
604 dependency.dstSubpass = 0;
605 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
606 dependency.srcAccessMask = 0;
607 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
608 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
609
610 array<VkAttachmentDescription, 2> attachments = { colorAttachment, depthAttachment };
611 VkRenderPassCreateInfo renderPassInfo = {};
612 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
613 renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
614 renderPassInfo.pAttachments = attachments.data();
615 renderPassInfo.subpassCount = 1;
616 renderPassInfo.pSubpasses = &subpass;
617 renderPassInfo.dependencyCount = 1;
618 renderPassInfo.pDependencies = &dependency;
619
620 if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
621 throw runtime_error("failed to create render pass!");
622 }
623}
624
625VkFormat VulkanGame::findDepthFormat() {
626 return VulkanUtils::findSupportedFormat(
627 physicalDevice,
628 { VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
629 VK_IMAGE_TILING_OPTIMAL,
630 VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
631 );
632}
633
[fa9fa1c]634void VulkanGame::createCommandPool() {
635 QueueFamilyIndices queueFamilyIndices = VulkanUtils::findQueueFamilies(physicalDevice, surface);;
636
637 VkCommandPoolCreateInfo poolInfo = {};
638 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
639 poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
640 poolInfo.flags = 0;
641
642 if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
643 throw runtime_error("failed to create graphics command pool!");
644 }
645}
646
[603b5bc]647void VulkanGame::createImageResources() {
648 VulkanUtils::createDepthImage(device, physicalDevice, commandPool, findDepthFormat(), swapChainExtent,
649 depthImage, graphicsQueue);
[b794178]650
[603b5bc]651 createTextureSampler();
[b794178]652
653 VulkanUtils::createVulkanImageFromFile(device, physicalDevice, commandPool, "textures/texture.jpg",
654 floorTextureImage, graphicsQueue);
655
656 floorTextureImageDescriptor = {};
657 floorTextureImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
658 floorTextureImageDescriptor.imageView = floorTextureImage.imageView;
659 floorTextureImageDescriptor.sampler = textureSampler;
660
[603b5bc]661 VulkanUtils::createVulkanImageFromSDLTexture(device, physicalDevice, uiOverlay, sdlOverlayImage);
662
[b794178]663 sdlOverlayImageDescriptor = {};
664 sdlOverlayImageDescriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
665 sdlOverlayImageDescriptor.imageView = sdlOverlayImage.imageView;
666 sdlOverlayImageDescriptor.sampler = textureSampler;
667}
668
669void VulkanGame::createTextureSampler() {
670 VkSamplerCreateInfo samplerInfo = {};
671 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
672 samplerInfo.magFilter = VK_FILTER_LINEAR;
673 samplerInfo.minFilter = VK_FILTER_LINEAR;
674
675 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT;
676 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;
677 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
678
679 samplerInfo.anisotropyEnable = VK_TRUE;
680 samplerInfo.maxAnisotropy = 16;
681 samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
682 samplerInfo.unnormalizedCoordinates = VK_FALSE;
683 samplerInfo.compareEnable = VK_FALSE;
684 samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
685 samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
686 samplerInfo.mipLodBias = 0.0f;
687 samplerInfo.minLod = 0.0f;
688 samplerInfo.maxLod = 0.0f;
689
690 if (vkCreateSampler(device, &samplerInfo, nullptr, &textureSampler) != VK_SUCCESS) {
691 throw runtime_error("failed to create texture sampler!");
692 }
693}
694
[603b5bc]695void VulkanGame::createFramebuffers() {
696 swapChainFramebuffers.resize(swapChainImageViews.size());
697
698 for (size_t i = 0; i < swapChainImageViews.size(); i++) {
699 array<VkImageView, 2> attachments = {
700 swapChainImageViews[i],
701 depthImage.imageView
702 };
703
704 VkFramebufferCreateInfo framebufferInfo = {};
705 framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
706 framebufferInfo.renderPass = renderPass;
707 framebufferInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
708 framebufferInfo.pAttachments = attachments.data();
709 framebufferInfo.width = swapChainExtent.width;
710 framebufferInfo.height = swapChainExtent.height;
711 framebufferInfo.layers = 1;
712
713 if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
714 throw runtime_error("failed to create framebuffer!");
715 }
716 }
717}
718
[b794178]719void VulkanGame::createUniformBuffers() {
720 VkDeviceSize bufferSize = sizeof(UniformBufferObject);
721
722 uniformBuffers.resize(swapChainImages.size());
723 uniformBuffersMemory.resize(swapChainImages.size());
724 uniformBufferInfoList.resize(swapChainImages.size());
725
726 for (size_t i = 0; i < swapChainImages.size(); i++) {
727 VulkanUtils::createBuffer(device, physicalDevice, bufferSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
728 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
729 uniformBuffers[i], uniformBuffersMemory[i]);
730
731 uniformBufferInfoList[i].buffer = uniformBuffers[i];
732 uniformBufferInfoList[i].offset = 0;
733 uniformBufferInfoList[i].range = sizeof(UniformBufferObject);
734 }
735}
736
[603b5bc]737void VulkanGame::createCommandBuffers() {
738 commandBuffers.resize(swapChainImages.size());
739
740 VkCommandBufferAllocateInfo allocInfo = {};
741 allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
742 allocInfo.commandPool = commandPool;
743 allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
744 allocInfo.commandBufferCount = (uint32_t) commandBuffers.size();
745
746 if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
747 throw runtime_error("failed to allocate command buffers!");
748 }
749
750 for (size_t i = 0; i < commandBuffers.size(); i++) {
751 VkCommandBufferBeginInfo beginInfo = {};
752 beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
753 beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
754 beginInfo.pInheritanceInfo = nullptr;
755
756 if (vkBeginCommandBuffer(commandBuffers[i], &beginInfo) != VK_SUCCESS) {
757 throw runtime_error("failed to begin recording command buffer!");
758 }
759
760 VkRenderPassBeginInfo renderPassInfo = {};
761 renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
762 renderPassInfo.renderPass = renderPass;
763 renderPassInfo.framebuffer = swapChainFramebuffers[i];
764 renderPassInfo.renderArea.offset = { 0, 0 };
765 renderPassInfo.renderArea.extent = swapChainExtent;
766
767 array<VkClearValue, 2> clearValues = {};
768 clearValues[0].color = {{ 0.0f, 0.0f, 0.0f, 1.0f }};
769 clearValues[1].depthStencil = { 1.0f, 0 };
770
771 renderPassInfo.clearValueCount = static_cast<uint32_t>(clearValues.size());
772 renderPassInfo.pClearValues = clearValues.data();
773
774 vkCmdBeginRenderPass(commandBuffers[i], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
775
776 // reateGraphicsPipelineCommands(scenePipeline, i);
777 // createGraphicsPipelineCommands(overlayPipeline, i);
778 for (GraphicsPipeline_Vulkan pipeline : graphicsPipelines) {
779 pipeline.createRenderCommands(commandBuffers[i], i);
780 }
781
782 vkCmdEndRenderPass(commandBuffers[i]);
783
784 if (vkEndCommandBuffer(commandBuffers[i]) != VK_SUCCESS) {
785 throw runtime_error("failed to record command buffer!");
786 }
787 }
788}
789
[34bdf3a]790void VulkanGame::createSyncObjects() {
791 imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
792 renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
793 inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
794
795 VkSemaphoreCreateInfo semaphoreInfo = {};
796 semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
797
798 VkFenceCreateInfo fenceInfo = {};
799 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
800 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
801
802 for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
803 if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS ||
804 vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS ||
805 vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {
806 throw runtime_error("failed to create synchronization objects for a frame!");
807 }
808 }
809}
810
[f94eea9]811void VulkanGame::cleanupSwapChain() {
[603b5bc]812 VulkanUtils::destroyVulkanImage(device, depthImage);
813
814 for (VkFramebuffer framebuffer : swapChainFramebuffers) {
815 vkDestroyFramebuffer(device, framebuffer, nullptr);
816 }
817
818 vkFreeCommandBuffers(device, commandPool, static_cast<uint32_t>(commandBuffers.size()), commandBuffers.data());
819
[b794178]820 for (GraphicsPipeline_Vulkan pipeline : graphicsPipelines) {
821 pipeline.cleanup();
822 }
823
[6fc24c7]824 vkDestroyRenderPass(device, renderPass, nullptr);
825
[b794178]826 for (VkImageView imageView : swapChainImageViews) {
[f94eea9]827 vkDestroyImageView(device, imageView, nullptr);
828 }
829
830 vkDestroySwapchainKHR(device, swapChain, nullptr);
[e83b155]831
[603b5bc]832 for (size_t i = 0; i < uniformBuffers.size(); i++) {
[e83b155]833 vkDestroyBuffer(device, uniformBuffers[i], nullptr);
834 vkFreeMemory(device, uniformBuffersMemory[i], nullptr);
835 }
[90a424f]836}
Note: See TracBrowser for help on using the repository browser.