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@…>, 4 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
Line 
1#include "vulkan-game.hpp"
2
3#include <array>
4#include <iostream>
5#include <set>
6
7#include "consts.hpp"
8#include "logger.hpp"
9
10#include "utils.hpp"
11
12using namespace std;
13
14struct UniformBufferObject {
15 alignas(16) mat4 model;
16 alignas(16) mat4 view;
17 alignas(16) mat4 proj;
18};
19
20VulkanGame::VulkanGame(int maxFramesInFlight) : MAX_FRAMES_IN_FLIGHT(maxFramesInFlight) {
21 gui = nullptr;
22 window = nullptr;
23
24 currentFrame = 0;
25 framebufferResized = false;
26}
27
28VulkanGame::~VulkanGame() {
29}
30
31void VulkanGame::run(int width, int height, unsigned char guiFlags) {
32 cout << "DEBUGGING IS " << (ENABLE_VALIDATION_LAYERS ? "ON" : "OFF") << endl;
33
34 cout << "Vulkan Game" << endl;
35
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
55 if (initWindow(width, height, guiFlags) == RTWO_ERROR) {
56 return;
57 }
58
59 initVulkan();
60 mainLoop();
61 cleanup();
62
63 close_log();
64}
65
66// TODO: Make some more initi functions, or call this initUI if the
67// amount of things initialized here keeps growing
68bool VulkanGame::initWindow(int width, int height, unsigned char guiFlags) {
69 // TODO: Put all fonts, textures, and images in the assets folder
70 gui = new GameGui_SDL();
71
72 if (gui->init() == RTWO_ERROR) {
73 // TODO: Also print these sorts of errors to the log
74 cout << "UI library could not be initialized!" << endl;
75 cout << gui->getError() << endl;
76 return RTWO_ERROR;
77 }
78
79 window = (SDL_Window*) gui->createWindow("Vulkan Game", width, height, guiFlags & GUI_FLAGS_WINDOW_FULLSCREEN);
80 if (window == nullptr) {
81 cout << "Window could not be created!" << endl;
82 cout << gui->getError() << endl;
83 return RTWO_ERROR;
84 }
85
86 cout << "Target window size: (" << width << ", " << height << ")" << endl;
87 cout << "Actual window size: (" << gui->getWindowWidth() << ", " << gui->getWindowHeight() << ")" << endl;
88
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
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
117 return RTWO_SUCCESS;
118}
119
120void VulkanGame::initVulkan() {
121 const vector<const char*> validationLayers = {
122 "VK_LAYER_KHRONOS_validation"
123 };
124 const vector<const char*> deviceExtensions = {
125 VK_KHR_SWAPCHAIN_EXTENSION_NAME
126 };
127
128 createVulkanInstance(validationLayers);
129 setupDebugMessenger();
130 createVulkanSurface();
131 pickPhysicalDevice(deviceExtensions);
132 createLogicalDevice(validationLayers, deviceExtensions);
133 createSwapChain();
134 createImageViews();
135 createRenderPass();
136 createCommandPool();
137
138 createImageResources();
139
140 createFramebuffers();
141 createUniformBuffers();
142
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,
160 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, sizeof(Vertex)));
161
162 graphicsPipelines.back().bindData(sceneVertices, sceneIndices, commandPool, graphicsQueue);
163
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
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();
174 graphicsPipelines.back().createPipeline("shaders/scene-vert.spv", "shaders/scene-frag.spv");
175 graphicsPipelines.back().createDescriptorPool(swapChainImages);
176 graphicsPipelines.back().createDescriptorSets(swapChainImages);
177
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,
189 { 0, 0, (int)swapChainExtent.width, (int)swapChainExtent.height }, sizeof(OverlayVertex)));
190
191 graphicsPipelines.back().bindData(overlayVertices, overlayIndices, commandPool, graphicsQueue);
192
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
196 graphicsPipelines.back().addDescriptorInfo(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
197 VK_SHADER_STAGE_FRAGMENT_BIT, &sdlOverlayImageDescriptor);
198
199 graphicsPipelines.back().createDescriptorSetLayout();
200 graphicsPipelines.back().createPipeline("shaders/overlay-vert.spv", "shaders/overlay-frag.spv");
201 graphicsPipelines.back().createDescriptorPool(swapChainImages);
202 graphicsPipelines.back().createDescriptorSets(swapChainImages);
203
204 // TODO: Creating the descriptor pool and descriptor sets might need to be redone when the
205 // swap chain is recreated
206
207 cout << "Created " << graphicsPipelines.size() << " graphics pipelines" << endl;
208
209 createCommandBuffers();
210
211 createSyncObjects();
212}
213
214void VulkanGame::mainLoop() {
215 UIEvent e;
216 bool quit = false;
217
218 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
219
220 while (!quit) {
221 gui->processEvents();
222
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;
233 case UI_EVENT_WINDOWRESIZE:
234 cout << "Window resize event detected" << endl;
235 framebufferResized = true;
236 break;
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;
252 case UI_EVENT_UNKNOWN:
253 cout << "Unknown event type: 0x" << hex << e.unknown.eventType << dec << endl;
254 break;
255 default:
256 cout << "Unhandled UI event: " << e.type << endl;
257 }
258 }
259
260 renderUI();
261 renderScene();
262 }
263
264 vkDeviceWaitIdle(device);
265}
266
267void VulkanGame::renderUI() {
268 SDL_RenderClear(renderer);
269 SDL_RenderPresent(renderer);
270}
271
272void VulkanGame::renderScene() {
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;
279}
280
281void VulkanGame::cleanup() {
282 cleanupSwapChain();
283
284 VulkanUtils::destroyVulkanImage(device, floorTextureImage);
285 VulkanUtils::destroyVulkanImage(device, sdlOverlayImage);
286
287 vkDestroySampler(device, textureSampler, nullptr);
288
289 for (GraphicsPipeline_Vulkan pipeline : graphicsPipelines) {
290 pipeline.cleanupBuffers();
291 }
292
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
299 vkDestroyCommandPool(device, commandPool, nullptr);
300 vkDestroyDevice(device, nullptr);
301 vkDestroySurfaceKHR(instance, surface, nullptr);
302
303 if (ENABLE_VALIDATION_LAYERS) {
304 VulkanUtils::destroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
305 }
306
307 vkDestroyInstance(instance, nullptr);
308
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
317 SDL_DestroyRenderer(renderer);
318 renderer = nullptr;
319
320 gui->destroyWindow();
321 gui->shutdown();
322 delete gui;
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}
402
403void VulkanGame::createVulkanSurface() {
404 if (gui->createVulkanSurface(instance, &surface) == RTWO_ERROR) {
405 throw runtime_error("failed to create window surface!");
406 }
407}
408
409void VulkanGame::pickPhysicalDevice(const vector<const char*>& deviceExtensions) {
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) {
422 if (isDeviceSuitable(device, deviceExtensions)) {
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
434bool VulkanGame::isDeviceSuitable(VkPhysicalDevice physicalDevice,
435 const vector<const char*>& deviceExtensions) {
436 VkPhysicalDeviceProperties deviceProperties;
437 vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
438
439 cout << "Device: " << deviceProperties.deviceName << endl;
440
441 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
442 bool extensionsSupported = VulkanUtils::checkDeviceExtensionSupport(physicalDevice, deviceExtensions);
443 bool swapChainAdequate = false;
444
445 if (extensionsSupported) {
446 SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
447 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
448 }
449
450 VkPhysicalDeviceFeatures supportedFeatures;
451 vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures);
452
453 return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
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
461 vector<VkDeviceQueueCreateInfo> queueCreateInfoList;
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
472 queueCreateInfoList.push_back(queueCreateInfo);
473 }
474
475 VkPhysicalDeviceFeatures deviceFeatures = {};
476 deviceFeatures.samplerAnisotropy = VK_TRUE;
477
478 VkDeviceCreateInfo createInfo = {};
479 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
480 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfoList.size());
481 createInfo.pQueueCreateInfos = queueCreateInfoList.data();
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);
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;
534 } else {
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;
555 swapChainExtent = extent;
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
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
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
647void VulkanGame::createImageResources() {
648 VulkanUtils::createDepthImage(device, physicalDevice, commandPool, findDepthFormat(), swapChainExtent,
649 depthImage, graphicsQueue);
650
651 createTextureSampler();
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
661 VulkanUtils::createVulkanImageFromSDLTexture(device, physicalDevice, uiOverlay, sdlOverlayImage);
662
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
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
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
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
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
811void VulkanGame::cleanupSwapChain() {
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
820 for (GraphicsPipeline_Vulkan pipeline : graphicsPipelines) {
821 pipeline.cleanup();
822 }
823
824 vkDestroyRenderPass(device, renderPass, nullptr);
825
826 for (VkImageView imageView : swapChainImageViews) {
827 vkDestroyImageView(device, imageView, nullptr);
828 }
829
830 vkDestroySwapchainKHR(device, swapChain, nullptr);
831
832 for (size_t i = 0; i < uniformBuffers.size(); i++) {
833 vkDestroyBuffer(device, uniformBuffers[i], nullptr);
834 vkFreeMemory(device, uniformBuffersMemory[i], nullptr);
835 }
836}
Note: See TracBrowser for help on using the repository browser.