source: opengl-game/vulkan-game.cpp@ 603b5bc

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

In vulkangame, add code to create the frame buffers and command buffers

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