source: opengl-game/sdl-game.cpp@ 6493e43

feature/imgui-sdl
Last change on this file since 6493e43 was 6493e43, checked in by Dmitry Portnoy <dportnoy@…>, 4 years ago

In sdl-game, add support for using separate graphics and present queues, and move the example ImGui code for (re)creating the swapchain into my own files

  • Property mode set to 100644
File size: 35.1 KB
Line 
1#include "sdl-game.hpp"
2
3#include <iostream>
4#include <set>
5#include <stdexcept>
6
7#include "IMGUI/imgui_impl_sdl.h"
8
9#include <stdio.h> // printf, fprintf
10#include <stdlib.h> // abort
11
12// dear imgui: standalone example application for SDL2 + Vulkan
13// If you are new to dear imgui, see examples/README.txt and documentation at the top of imgui.cpp.
14
15// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app.
16// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h.
17// You will use those if you want to use this rendering back-end in your engine/app.
18// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by
19// the back-end itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code.
20// Read comments in imgui_impl_vulkan.h.
21
22#include <SDL2/SDL_vulkan.h>
23
24#include "logger.hpp"
25
26#include "vulkan-utils.hpp"
27
28using namespace std;
29
30//#define IMGUI_UNLIMITED_FRAME_RATE
31
32static VkAllocationCallbacks* g_Allocator = NULL;
33static VkInstance g_Instance = VK_NULL_HANDLE;
34static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE;
35static VkDevice g_Device = VK_NULL_HANDLE;
36static VkQueue g_GraphicsQueue = VK_NULL_HANDLE;
37static VkQueue g_PresentQueue = VK_NULL_HANDLE;
38static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE;
39
40static ImGui_ImplVulkanH_Window g_MainWindowData;
41static uint32_t g_MinImageCount = 2;
42static bool g_SwapChainRebuild = false;
43
44static void check_vk_result(VkResult err)
45{
46 if (err == 0)
47 return;
48 fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err);
49 if (err < 0)
50 abort();
51}
52
53// All the ImGui_ImplVulkanH_XXX structures/functions are optional helpers used by the demo.
54// Your real engine/app may not use them.
55
56static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, SDL_Window* window) {
57 // TODO: SetupVulkanWIndow calls vkGetPhysicalDeviceSurfaceSupportKHR to get the present queue. In vulkan-game, I do this in findQueueFamilies.
58 int width, height;
59 SDL_GetWindowSize(window, &width, &height);
60
61 wd->Surface = surface;
62
63 // Select Surface Format
64 const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM };
65 const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
66 wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace);
67
68 // Select Present Mode
69#ifdef IMGUI_UNLIMITED_FRAME_RATE
70 VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR };
71#else
72 VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_FIFO_KHR };
73#endif
74 wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_modes[0], IM_ARRAYSIZE(present_modes));
75 //printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode);
76
77 // Create SwapChain, RenderPass, Framebuffer, etc.
78 IM_ASSERT(g_MinImageCount >= 2);
79
80 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(g_PhysicalDevice, surface);
81
82 ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, indices.graphicsFamily.value(),
83 g_Allocator, width, height, g_MinImageCount);
84}
85
86static void CleanupVulkanWindow()
87{
88 ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, &g_MainWindowData, g_Allocator);
89}
90
91static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data)
92{
93 VkResult err;
94
95 VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore;
96 VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore;
97 err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex);
98 if (err == VK_ERROR_OUT_OF_DATE_KHR)
99 {
100 g_SwapChainRebuild = true;
101 return;
102 }
103 check_vk_result(err);
104
105 ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex];
106 {
107 err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking
108 check_vk_result(err);
109
110 err = vkResetFences(g_Device, 1, &fd->Fence);
111 check_vk_result(err);
112 }
113 {
114 err = vkResetCommandPool(g_Device, fd->CommandPool, 0);
115 check_vk_result(err);
116 VkCommandBufferBeginInfo info = {};
117 info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
118 info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
119 err = vkBeginCommandBuffer(fd->CommandBuffer, &info);
120 check_vk_result(err);
121 }
122 {
123 VkRenderPassBeginInfo info = {};
124 info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
125 info.renderPass = wd->RenderPass;
126 info.framebuffer = fd->Framebuffer;
127 info.renderArea.extent.width = wd->Width;
128 info.renderArea.extent.height = wd->Height;
129 info.clearValueCount = 1;
130 info.pClearValues = &wd->ClearValue;
131 vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE);
132 }
133
134 // Record dear imgui primitives into command buffer
135 ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer);
136
137 // Submit command buffer
138 vkCmdEndRenderPass(fd->CommandBuffer);
139 {
140 VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
141 VkSubmitInfo info = {};
142 info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
143 info.waitSemaphoreCount = 1;
144 info.pWaitSemaphores = &image_acquired_semaphore;
145 info.pWaitDstStageMask = &wait_stage;
146 info.commandBufferCount = 1;
147 info.pCommandBuffers = &fd->CommandBuffer;
148 info.signalSemaphoreCount = 1;
149 info.pSignalSemaphores = &render_complete_semaphore;
150
151 err = vkEndCommandBuffer(fd->CommandBuffer);
152 check_vk_result(err);
153 err = vkQueueSubmit(g_GraphicsQueue, 1, &info, fd->Fence);
154 check_vk_result(err);
155 }
156}
157
158static void FramePresent(ImGui_ImplVulkanH_Window* wd)
159{
160 if (g_SwapChainRebuild)
161 return;
162 VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore;
163 VkPresentInfoKHR info = {};
164 info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
165 info.waitSemaphoreCount = 1;
166 info.pWaitSemaphores = &render_complete_semaphore;
167 info.swapchainCount = 1;
168 info.pSwapchains = &wd->Swapchain;
169 info.pImageIndices = &wd->FrameIndex;
170 VkResult err = vkQueuePresentKHR(g_PresentQueue, &info);
171 if (err == VK_ERROR_OUT_OF_DATE_KHR)
172 {
173 g_SwapChainRebuild = true;
174 return;
175 }
176 check_vk_result(err);
177 wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->ImageCount; // Now we can use the next set of semaphores
178}
179
180/********************************************* START OF NEW CODE *********************************************/
181
182VKAPI_ATTR VkBool32 VKAPI_CALL VulkanGame::debugCallback(
183 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
184 VkDebugUtilsMessageTypeFlagsEXT messageType,
185 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
186 void* pUserData) {
187 cerr << "validation layer: " << pCallbackData->pMessage << endl;
188
189 return VK_FALSE;
190}
191
192VulkanGame::VulkanGame(int maxFramesInFlight) : MAX_FRAMES_IN_FLIGHT(maxFramesInFlight) {
193 this->gui = nullptr;
194 this->window = nullptr;
195}
196
197VulkanGame::~VulkanGame() {
198}
199
200void VulkanGame::run(int width, int height, unsigned char guiFlags) {
201 cout << "DEBUGGING IS " << (ENABLE_VALIDATION_LAYERS ? "ON" : "OFF") << endl;
202
203 cout << "Vulkan Game" << endl;
204
205 if (initUI(width, height, guiFlags) == RTWO_ERROR) {
206 return;
207 }
208
209 initVulkan();
210
211 VkResult err;
212
213 // Create Framebuffers
214 ImGui_ImplVulkanH_Window* wd = &g_MainWindowData;
215 SetupVulkanWindow(wd, surface, window);
216
217 // Create Descriptor Pool
218 {
219 VkDescriptorPoolSize pool_sizes[] =
220 {
221 { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
222 { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
223 { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
224 { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
225 { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
226 { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
227 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
228 { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
229 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
230 { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
231 { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }
232 };
233 VkDescriptorPoolCreateInfo pool_info = {};
234 pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
235 pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
236 pool_info.maxSets = 1000 * IM_ARRAYSIZE(pool_sizes);
237 pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes);
238 pool_info.pPoolSizes = pool_sizes;
239 err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &descriptorPool);
240 check_vk_result(err);
241 }
242
243 // Setup Dear ImGui context
244 IMGUI_CHECKVERSION();
245 ImGui::CreateContext();
246 ImGuiIO& io = ImGui::GetIO(); (void)io;
247 //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
248 //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
249
250 // Setup Dear ImGui style
251 ImGui::StyleColorsDark();
252 //ImGui::StyleColorsClassic();
253
254 // Setup Platform/Renderer bindings
255 ImGui_ImplSDL2_InitForVulkan(window);
256 ImGui_ImplVulkan_InitInfo init_info = {};
257 init_info.Instance = g_Instance;
258 init_info.PhysicalDevice = g_PhysicalDevice;
259 init_info.Device = g_Device;
260 init_info.PipelineCache = g_PipelineCache;
261 init_info.DescriptorPool = descriptorPool;
262 init_info.Allocator = g_Allocator;
263 init_info.MinImageCount = g_MinImageCount;
264 init_info.ImageCount = wd->ImageCount;
265 init_info.CheckVkResultFn = check_vk_result;
266 ImGui_ImplVulkan_Init(&init_info, wd->RenderPass);
267
268 // Load Fonts
269 // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
270 // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
271 // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
272 // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
273 // - Read 'docs/FONTS.md' for more instructions and details.
274 // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
275 //io.Fonts->AddFontDefault();
276 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
277 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
278 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
279 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f);
280 //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese());
281 //IM_ASSERT(font != NULL);
282
283 // Upload Fonts
284 {
285 // Use any command queue
286 VkCommandPool command_pool = wd->Frames[wd->FrameIndex].CommandPool;
287 VkCommandBuffer command_buffer = wd->Frames[wd->FrameIndex].CommandBuffer;
288
289 err = vkResetCommandPool(g_Device, command_pool, 0);
290 check_vk_result(err);
291 VkCommandBufferBeginInfo begin_info = {};
292 begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
293 begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
294 err = vkBeginCommandBuffer(command_buffer, &begin_info);
295 check_vk_result(err);
296
297 ImGui_ImplVulkan_CreateFontsTexture(command_buffer);
298
299 VkSubmitInfo end_info = {};
300 end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
301 end_info.commandBufferCount = 1;
302 end_info.pCommandBuffers = &command_buffer;
303 err = vkEndCommandBuffer(command_buffer);
304 check_vk_result(err);
305 err = vkQueueSubmit(graphicsQueue, 1, &end_info, VK_NULL_HANDLE);
306 check_vk_result(err);
307
308 err = vkDeviceWaitIdle(g_Device);
309 check_vk_result(err);
310 ImGui_ImplVulkan_DestroyFontUploadObjects();
311 }
312
313 // Our state
314 bool show_demo_window = true;
315 bool show_another_window = false;
316 ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
317
318 // Main loop
319 bool done = false;
320 while (!done) {
321 // Poll and handle events (inputs, window resize, etc.)
322 // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
323 // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
324 // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
325 // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
326 SDL_Event event;
327 while (SDL_PollEvent(&event)) {
328 ImGui_ImplSDL2_ProcessEvent(&event);
329 if (event.type == SDL_QUIT)
330 done = true;
331 if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))
332 done = true;
333 }
334
335 // Resize swap chain?
336 if (g_SwapChainRebuild) {
337 int width, height;
338 SDL_GetWindowSize(window, &width, &height);
339 if (width > 0 && height > 0)
340 {
341 ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount);
342
343 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(g_PhysicalDevice, surface);
344
345 ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, &g_MainWindowData,
346 indices.graphicsFamily.value(), g_Allocator, width, height, g_MinImageCount);
347 g_MainWindowData.FrameIndex = 0;
348 g_SwapChainRebuild = false;
349 }
350 }
351
352 // Start the Dear ImGui frame
353 ImGui_ImplVulkan_NewFrame();
354 ImGui_ImplSDL2_NewFrame(window);
355 ImGui::NewFrame();
356
357 // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
358 if (show_demo_window)
359 ImGui::ShowDemoWindow(&show_demo_window);
360
361 // 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window.
362 {
363 static float f = 0.0f;
364 static int counter = 0;
365
366 ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
367
368 ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
369 ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state
370 ImGui::Checkbox("Another Window", &show_another_window);
371
372 ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
373 ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
374
375 if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
376 counter++;
377 ImGui::SameLine();
378 ImGui::Text("counter = %d", counter);
379
380 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
381 ImGui::End();
382 }
383
384 // 3. Show another simple window.
385 if (show_another_window)
386 {
387 ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked)
388 ImGui::Text("Hello from another window!");
389 if (ImGui::Button("Close Me"))
390 show_another_window = false;
391 ImGui::End();
392 }
393
394 // Rendering
395 ImGui::Render();
396 ImDrawData* draw_data = ImGui::GetDrawData();
397 const bool is_minimized = (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f);
398 if (!is_minimized)
399 {
400 memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float));
401 FrameRender(wd, draw_data);
402 FramePresent(wd);
403 }
404 }
405
406 // Cleanup
407 err = vkDeviceWaitIdle(g_Device);
408 check_vk_result(err);
409 ImGui_ImplVulkan_Shutdown();
410 ImGui_ImplSDL2_Shutdown();
411 ImGui::DestroyContext();
412
413 CleanupVulkanWindow();
414 cleanup();
415
416 close_log();
417}
418
419bool VulkanGame::initUI(int width, int height, unsigned char guiFlags) {
420 // TODO: Create a game-gui function to get the gui version and retrieve it that way
421
422 SDL_VERSION(&sdlVersion); // This gets the compile-time version
423 SDL_GetVersion(&sdlVersion); // This gets the runtime version
424
425 cout << "SDL " <<
426 to_string(sdlVersion.major) << "." <<
427 to_string(sdlVersion.minor) << "." <<
428 to_string(sdlVersion.patch) << endl;
429
430 // TODO: Refactor the logger api to be more flexible,
431 // esp. since gl_log() and gl_log_err() have issues printing anything besides strings
432 restart_gl_log();
433 gl_log("starting SDL\n%s.%s.%s",
434 to_string(sdlVersion.major).c_str(),
435 to_string(sdlVersion.minor).c_str(),
436 to_string(sdlVersion.patch).c_str());
437
438 // TODO: Use open_Log() and related functions instead of gl_log ones
439 // TODO: In addition, delete the gl_log functions
440 open_log();
441 get_log() << "starting SDL" << endl;
442 get_log() <<
443 (int)sdlVersion.major << "." <<
444 (int)sdlVersion.minor << "." <<
445 (int)sdlVersion.patch << endl;
446
447 // TODO: Put all fonts, textures, and images in the assets folder
448 gui = new GameGui_SDL();
449
450 if (gui->init() == RTWO_ERROR) {
451 // TODO: Also print these sorts of errors to the log
452 cout << "UI library could not be initialized!" << endl;
453 cout << gui->getError() << endl;
454 return RTWO_ERROR;
455 }
456
457 window = (SDL_Window*)gui->createWindow("Vulkan Game", width, height, guiFlags & GUI_FLAGS_WINDOW_FULLSCREEN);
458 if (window == nullptr) {
459 cout << "Window could not be created!" << endl;
460 cout << gui->getError() << endl;
461 return RTWO_ERROR;
462 }
463
464 cout << "Target window size: (" << width << ", " << height << ")" << endl;
465 cout << "Actual window size: (" << gui->getWindowWidth() << ", " << gui->getWindowHeight() << ")" << endl;
466
467 return RTWO_SUCCESS;
468}
469
470void VulkanGame::initVulkan() {
471 const vector<const char*> validationLayers = {
472 "VK_LAYER_KHRONOS_validation"
473 };
474 const vector<const char*> deviceExtensions = {
475 VK_KHR_SWAPCHAIN_EXTENSION_NAME
476 };
477
478 createVulkanInstance(validationLayers);
479 setupDebugMessenger();
480 createVulkanSurface();
481 pickPhysicalDevice(deviceExtensions);
482 createLogicalDevice(validationLayers, deviceExtensions);
483}
484
485void VulkanGame::cleanup() {
486 vkDestroyDescriptorPool(g_Device, descriptorPool, g_Allocator);
487
488 if (ENABLE_VALIDATION_LAYERS) {
489 VulkanUtils::destroyDebugUtilsMessengerEXT(g_Instance, debugMessenger, nullptr);
490 }
491
492 vkDestroyDevice(g_Device, g_Allocator);
493 vkDestroyInstance(g_Instance, g_Allocator);
494
495 gui->destroyWindow();
496 gui->shutdown();
497 delete gui;
498}
499
500void VulkanGame::createVulkanInstance(const vector<const char*>& validationLayers) {
501 if (ENABLE_VALIDATION_LAYERS && !VulkanUtils::checkValidationLayerSupport(validationLayers)) {
502 throw runtime_error("validation layers requested, but not available!");
503 }
504
505 VkApplicationInfo appInfo = {};
506 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
507 appInfo.pApplicationName = "Vulkan Game";
508 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
509 appInfo.pEngineName = "No Engine";
510 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
511 appInfo.apiVersion = VK_API_VERSION_1_0;
512
513 VkInstanceCreateInfo createInfo = {};
514 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
515 createInfo.pApplicationInfo = &appInfo;
516
517 vector<const char*> extensions = gui->getRequiredExtensions();
518 if (ENABLE_VALIDATION_LAYERS) {
519 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
520 }
521
522 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
523 createInfo.ppEnabledExtensionNames = extensions.data();
524
525 cout << endl << "Extensions:" << endl;
526 for (const char* extensionName : extensions) {
527 cout << extensionName << endl;
528 }
529 cout << endl;
530
531 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
532 if (ENABLE_VALIDATION_LAYERS) {
533 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
534 createInfo.ppEnabledLayerNames = validationLayers.data();
535
536 populateDebugMessengerCreateInfo(debugCreateInfo);
537 createInfo.pNext = &debugCreateInfo;
538 } else {
539 createInfo.enabledLayerCount = 0;
540
541 createInfo.pNext = nullptr;
542 }
543
544 if (vkCreateInstance(&createInfo, nullptr, &g_Instance) != VK_SUCCESS) {
545 throw runtime_error("failed to create instance!");
546 }
547}
548
549void VulkanGame::setupDebugMessenger() {
550 if (!ENABLE_VALIDATION_LAYERS) {
551 return;
552 }
553
554 VkDebugUtilsMessengerCreateInfoEXT createInfo;
555 populateDebugMessengerCreateInfo(createInfo);
556
557 if (VulkanUtils::createDebugUtilsMessengerEXT(g_Instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
558 throw runtime_error("failed to set up debug messenger!");
559 }
560}
561
562void VulkanGame::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
563 createInfo = {};
564 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
565 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;
566 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;
567 createInfo.pfnUserCallback = debugCallback;
568}
569
570void VulkanGame::createVulkanSurface() {
571 if (gui->createVulkanSurface(g_Instance, &surface) == RTWO_ERROR) {
572 throw runtime_error("failed to create window surface!");
573 }
574}
575
576void VulkanGame::pickPhysicalDevice(const vector<const char*>& deviceExtensions) {
577 uint32_t deviceCount = 0;
578 // TODO: Check VkResult
579 vkEnumeratePhysicalDevices(g_Instance, &deviceCount, nullptr);
580
581 if (deviceCount == 0) {
582 throw runtime_error("failed to find GPUs with Vulkan support!");
583 }
584
585 vector<VkPhysicalDevice> devices(deviceCount);
586 // TODO: Check VkResult
587 vkEnumeratePhysicalDevices(g_Instance, &deviceCount, devices.data());
588
589 cout << endl << "Graphics cards:" << endl;
590 for (const VkPhysicalDevice& device : devices) {
591 if (isDeviceSuitable(device, deviceExtensions)) {
592 g_PhysicalDevice = device;
593 break;
594 }
595 }
596 cout << endl;
597
598 if (g_PhysicalDevice == VK_NULL_HANDLE) {
599 throw runtime_error("failed to find a suitable GPU!");
600 }
601}
602
603bool VulkanGame::isDeviceSuitable(VkPhysicalDevice physicalDevice, const vector<const char*>& deviceExtensions) {
604 VkPhysicalDeviceProperties deviceProperties;
605 vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
606
607 cout << "Device: " << deviceProperties.deviceName << endl;
608
609 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
610 bool extensionsSupported = VulkanUtils::checkDeviceExtensionSupport(physicalDevice, deviceExtensions);
611 bool swapChainAdequate = false;
612
613 if (extensionsSupported) {
614 SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
615 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
616 }
617
618 VkPhysicalDeviceFeatures supportedFeatures;
619 vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures);
620
621 return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
622}
623
624void VulkanGame::createLogicalDevice(const vector<const char*>& validationLayers,
625 const vector<const char*>& deviceExtensions) {
626 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(g_PhysicalDevice, surface);
627
628 if (!indices.isComplete()) {
629 throw runtime_error("failed to find required queue families!");
630 }
631
632 // TODO: Using separate graphics and present queues currently works, but I should verify that I'm
633 // using them correctly to get the most benefit out of separate queues
634
635 vector<VkDeviceQueueCreateInfo> queueCreateInfoList;
636 set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
637
638 float queuePriority = 1.0f;
639 for (uint32_t queueFamily : uniqueQueueFamilies) {
640 VkDeviceQueueCreateInfo queueCreateInfo = {};
641 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
642 queueCreateInfo.queueCount = 1;
643 queueCreateInfo.queueFamilyIndex = queueFamily;
644 queueCreateInfo.pQueuePriorities = &queuePriority;
645
646 queueCreateInfoList.push_back(queueCreateInfo);
647 }
648
649 VkPhysicalDeviceFeatures deviceFeatures = {};
650 deviceFeatures.samplerAnisotropy = VK_TRUE;
651
652 VkDeviceCreateInfo createInfo = {};
653 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
654
655 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfoList.size());
656 createInfo.pQueueCreateInfos = queueCreateInfoList.data();
657
658 createInfo.pEnabledFeatures = &deviceFeatures;
659
660 createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
661 createInfo.ppEnabledExtensionNames = deviceExtensions.data();
662
663 // These fields are ignored by up-to-date Vulkan implementations,
664 // but it's a good idea to set them for backwards compatibility
665 if (ENABLE_VALIDATION_LAYERS) {
666 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
667 createInfo.ppEnabledLayerNames = validationLayers.data();
668 } else {
669 createInfo.enabledLayerCount = 0;
670 }
671
672 if (vkCreateDevice(g_PhysicalDevice, &createInfo, nullptr, &g_Device) != VK_SUCCESS) {
673 throw runtime_error("failed to create logical device!");
674 }
675
676 vkGetDeviceQueue(g_Device, indices.graphicsFamily.value(), 0, &graphicsQueue);
677 vkGetDeviceQueue(g_Device, indices.presentFamily.value(), 0, &presentQueue);
678
679 g_GraphicsQueue = graphicsQueue;
680 g_PresentQueue = presentQueue;
681}
682
683/********************************************** END OF NEW CODE **********************************************/
684
685/********************************************** START TEMP HELPER GUNVTIONS **********************************/
686
687// Forward Declarations
688void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd,
689 const VkAllocationCallbacks* allocator);
690void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkDevice device, ImGui_ImplVulkanH_FrameSemaphores* fsd,
691 const VkAllocationCallbacks* allocator);
692void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device,
693 ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator);
694
695// Create or resize window
696void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device,
697 ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int width,
698 int height, uint32_t min_image_count) {
699 (void)instance;
700 ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count);
701 ImGui_ImplVulkanH_CreateWindowCommandBuffers(physical_device, device, wd, queue_family, allocator);
702}
703
704// Also destroy old swap chain and in-flight frames data, if any.
705void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device,
706 ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count) {
707 VkResult err;
708 VkSwapchainKHR old_swapchain = wd->Swapchain;
709 wd->Swapchain = NULL;
710 err = vkDeviceWaitIdle(device);
711 check_vk_result(err);
712
713 // We don't use ImGui_ImplVulkanH_DestroyWindow() because we want to preserve the old swapchain to create the new one.
714 // Destroy old Framebuffer
715 for (uint32_t i = 0; i < wd->ImageCount; i++) {
716 ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator);
717 ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator);
718 }
719 IM_FREE(wd->Frames);
720 IM_FREE(wd->FrameSemaphores);
721 wd->Frames = NULL;
722 wd->FrameSemaphores = NULL;
723 wd->ImageCount = 0;
724 if (wd->RenderPass) {
725 vkDestroyRenderPass(device, wd->RenderPass, allocator);
726 }
727 if (wd->Pipeline) {
728 vkDestroyPipeline(device, wd->Pipeline, allocator);
729 }
730
731 // If min image count was not specified, request different count of images dependent on selected present mode
732 if (min_image_count == 0) {
733 min_image_count = ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(wd->PresentMode);
734 }
735
736 // Create Swapchain
737 {
738 VkSwapchainCreateInfoKHR info = {};
739 info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
740 info.surface = wd->Surface;
741 info.minImageCount = min_image_count;
742 info.imageFormat = wd->SurfaceFormat.format;
743 info.imageColorSpace = wd->SurfaceFormat.colorSpace;
744 info.imageArrayLayers = 1;
745 info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
746 info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // Assume that graphics family == present family
747 info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
748 info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
749 info.presentMode = wd->PresentMode;
750 info.clipped = VK_TRUE;
751 info.oldSwapchain = old_swapchain;
752 VkSurfaceCapabilitiesKHR cap;
753 err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, wd->Surface, &cap);
754 check_vk_result(err);
755 if (info.minImageCount < cap.minImageCount) {
756 info.minImageCount = cap.minImageCount;
757 } else if (cap.maxImageCount != 0 && info.minImageCount > cap.maxImageCount) {
758 info.minImageCount = cap.maxImageCount;
759 }
760
761 if (cap.currentExtent.width == 0xffffffff) {
762 info.imageExtent.width = wd->Width = w;
763 info.imageExtent.height = wd->Height = h;
764 } else {
765 info.imageExtent.width = wd->Width = cap.currentExtent.width;
766 info.imageExtent.height = wd->Height = cap.currentExtent.height;
767 }
768 err = vkCreateSwapchainKHR(device, &info, allocator, &wd->Swapchain);
769 check_vk_result(err);
770 err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->ImageCount, NULL);
771 check_vk_result(err);
772 VkImage backbuffers[16] = {};
773 IM_ASSERT(wd->ImageCount >= min_image_count);
774 IM_ASSERT(wd->ImageCount < IM_ARRAYSIZE(backbuffers));
775 err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->ImageCount, backbuffers);
776 check_vk_result(err);
777
778 IM_ASSERT(wd->Frames == NULL);
779 wd->Frames = (ImGui_ImplVulkanH_Frame*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_Frame) * wd->ImageCount);
780 wd->FrameSemaphores = (ImGui_ImplVulkanH_FrameSemaphores*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_FrameSemaphores) * wd->ImageCount);
781 memset(wd->Frames, 0, sizeof(wd->Frames[0]) * wd->ImageCount);
782 memset(wd->FrameSemaphores, 0, sizeof(wd->FrameSemaphores[0]) * wd->ImageCount);
783 for (uint32_t i = 0; i < wd->ImageCount; i++) {
784 wd->Frames[i].Backbuffer = backbuffers[i];
785 }
786 }
787 if (old_swapchain) {
788 vkDestroySwapchainKHR(device, old_swapchain, allocator);
789 }
790
791 // Create the Render Pass
792 {
793 VkAttachmentDescription attachment = {};
794 attachment.format = wd->SurfaceFormat.format;
795 attachment.samples = VK_SAMPLE_COUNT_1_BIT;
796 attachment.loadOp = wd->ClearEnable ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
797 attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
798 attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
799 attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
800 attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
801 attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
802 VkAttachmentReference color_attachment = {};
803 color_attachment.attachment = 0;
804 color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
805 VkSubpassDescription subpass = {};
806 subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
807 subpass.colorAttachmentCount = 1;
808 subpass.pColorAttachments = &color_attachment;
809 VkSubpassDependency dependency = {};
810 dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
811 dependency.dstSubpass = 0;
812 dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
813 dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
814 dependency.srcAccessMask = 0;
815 dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
816 VkRenderPassCreateInfo info = {};
817 info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
818 info.attachmentCount = 1;
819 info.pAttachments = &attachment;
820 info.subpassCount = 1;
821 info.pSubpasses = &subpass;
822 info.dependencyCount = 1;
823 info.pDependencies = &dependency;
824 err = vkCreateRenderPass(device, &info, allocator, &wd->RenderPass);
825 check_vk_result(err);
826
827 // We do not create a pipeline by default as this is also used by examples' main.cpp,
828 // but secondary viewport in multi-viewport mode may want to create one with:
829 //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline);
830 }
831
832 // Create The Image Views
833 {
834 VkImageViewCreateInfo info = {};
835 info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
836 info.viewType = VK_IMAGE_VIEW_TYPE_2D;
837 info.format = wd->SurfaceFormat.format;
838 info.components.r = VK_COMPONENT_SWIZZLE_R;
839 info.components.g = VK_COMPONENT_SWIZZLE_G;
840 info.components.b = VK_COMPONENT_SWIZZLE_B;
841 info.components.a = VK_COMPONENT_SWIZZLE_A;
842 VkImageSubresourceRange image_range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
843 info.subresourceRange = image_range;
844 for (uint32_t i = 0; i < wd->ImageCount; i++) {
845 ImGui_ImplVulkanH_Frame* fd = &wd->Frames[i];
846 info.image = fd->Backbuffer;
847 err = vkCreateImageView(device, &info, allocator, &fd->BackbufferView);
848 check_vk_result(err);
849 }
850 }
851
852 // Create Framebuffer
853 {
854 VkImageView attachment[1];
855 VkFramebufferCreateInfo info = {};
856 info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
857 info.renderPass = wd->RenderPass;
858 info.attachmentCount = 1;
859 info.pAttachments = attachment;
860 info.width = wd->Width;
861 info.height = wd->Height;
862 info.layers = 1;
863 for (uint32_t i = 0; i < wd->ImageCount; i++) {
864 ImGui_ImplVulkanH_Frame* fd = &wd->Frames[i];
865 attachment[0] = fd->BackbufferView;
866 err = vkCreateFramebuffer(device, &info, allocator, &fd->Framebuffer);
867 check_vk_result(err);
868 }
869 }
870}
871
872/*********************************************** END TEMP HELPER GUNVTIONS ***********************************/
Note: See TracBrowser for help on using the repository browser.