source: opengl-game/sdl-game.cpp@ 3b7d497

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

Start implementing an ImGUI ui on top of SDL and Vulkan using some example code

  • Property mode set to 100644
File size: 26.5 KB
Line 
1#include "sdl-game.hpp"
2
3#include <iostream>
4#include <set>
5#include <stdexcept>
6
7// dear imgui: standalone example application for SDL2 + Vulkan
8// If you are new to dear imgui, see examples/README.txt and documentation at the top of imgui.cpp.
9
10// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app.
11// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h.
12// You will use those if you want to use this rendering back-end in your engine/app.
13// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by
14// the back-end itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code.
15// Read comments in imgui_impl_vulkan.h.
16
17#include "IMGUI/imgui.h"
18#include "IMGUI/imgui_impl_sdl.h"
19#include "IMGUI/imgui_impl_vulkan.h"
20#include <stdio.h> // printf, fprintf
21#include <stdlib.h> // abort
22
23#include <SDL2/SDL_vulkan.h>
24
25#include "logger.hpp"
26
27#include "vulkan-utils.hpp"
28
29using namespace std;
30
31//#define IMGUI_UNLIMITED_FRAME_RATE
32
33static VkAllocationCallbacks* g_Allocator = NULL;
34static VkInstance g_Instance = VK_NULL_HANDLE;
35static VkPhysicalDevice g_PhysicalDevice = VK_NULL_HANDLE;
36static VkDevice g_Device = VK_NULL_HANDLE;
37static uint32_t g_QueueFamily = (uint32_t)-1;
38static VkQueue g_Queue = VK_NULL_HANDLE;
39static VkPipelineCache g_PipelineCache = VK_NULL_HANDLE;
40static VkDescriptorPool g_DescriptorPool = VK_NULL_HANDLE;
41
42static ImGui_ImplVulkanH_Window g_MainWindowData;
43static uint32_t g_MinImageCount = 2;
44static bool g_SwapChainRebuild = false;
45
46static void check_vk_result(VkResult err)
47{
48 if (err == 0)
49 return;
50 fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err);
51 if (err < 0)
52 abort();
53}
54
55// All the ImGui_ImplVulkanH_XXX structures/functions are optional helpers used by the demo.
56// Your real engine/app may not use them.
57static void SetupVulkanWindow(ImGui_ImplVulkanH_Window* wd, VkSurfaceKHR surface, int width, int height)
58{
59 wd->Surface = surface;
60
61 // Check for WSI support
62 VkBool32 res;
63 vkGetPhysicalDeviceSurfaceSupportKHR(g_PhysicalDevice, g_QueueFamily, wd->Surface, &res);
64 if (res != VK_TRUE)
65 {
66 fprintf(stderr, "Error no WSI support on physical device 0\n");
67 exit(-1);
68 }
69
70 // Select Surface Format
71 const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM };
72 const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;
73 wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(g_PhysicalDevice, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace);
74
75 // Select Present Mode
76#ifdef IMGUI_UNLIMITED_FRAME_RATE
77 VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR, VK_PRESENT_MODE_FIFO_KHR };
78#else
79 VkPresentModeKHR present_modes[] = { VK_PRESENT_MODE_FIFO_KHR };
80#endif
81 wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(g_PhysicalDevice, wd->Surface, &present_modes[0], IM_ARRAYSIZE(present_modes));
82 //printf("[vulkan] Selected PresentMode = %d\n", wd->PresentMode);
83
84 // Create SwapChain, RenderPass, Framebuffer, etc.
85 IM_ASSERT(g_MinImageCount >= 2);
86 ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, wd, g_QueueFamily, g_Allocator, width, height, g_MinImageCount);
87}
88
89static void CleanupVulkanWindow()
90{
91 ImGui_ImplVulkanH_DestroyWindow(g_Instance, g_Device, &g_MainWindowData, g_Allocator);
92}
93
94static void FrameRender(ImGui_ImplVulkanH_Window* wd, ImDrawData* draw_data)
95{
96 VkResult err;
97
98 VkSemaphore image_acquired_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore;
99 VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore;
100 err = vkAcquireNextImageKHR(g_Device, wd->Swapchain, UINT64_MAX, image_acquired_semaphore, VK_NULL_HANDLE, &wd->FrameIndex);
101 if (err == VK_ERROR_OUT_OF_DATE_KHR)
102 {
103 g_SwapChainRebuild = true;
104 return;
105 }
106 check_vk_result(err);
107
108 ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex];
109 {
110 err = vkWaitForFences(g_Device, 1, &fd->Fence, VK_TRUE, UINT64_MAX); // wait indefinitely instead of periodically checking
111 check_vk_result(err);
112
113 err = vkResetFences(g_Device, 1, &fd->Fence);
114 check_vk_result(err);
115 }
116 {
117 err = vkResetCommandPool(g_Device, fd->CommandPool, 0);
118 check_vk_result(err);
119 VkCommandBufferBeginInfo info = {};
120 info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
121 info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
122 err = vkBeginCommandBuffer(fd->CommandBuffer, &info);
123 check_vk_result(err);
124 }
125 {
126 VkRenderPassBeginInfo info = {};
127 info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
128 info.renderPass = wd->RenderPass;
129 info.framebuffer = fd->Framebuffer;
130 info.renderArea.extent.width = wd->Width;
131 info.renderArea.extent.height = wd->Height;
132 info.clearValueCount = 1;
133 info.pClearValues = &wd->ClearValue;
134 vkCmdBeginRenderPass(fd->CommandBuffer, &info, VK_SUBPASS_CONTENTS_INLINE);
135 }
136
137 // Record dear imgui primitives into command buffer
138 ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer);
139
140 // Submit command buffer
141 vkCmdEndRenderPass(fd->CommandBuffer);
142 {
143 VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
144 VkSubmitInfo info = {};
145 info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
146 info.waitSemaphoreCount = 1;
147 info.pWaitSemaphores = &image_acquired_semaphore;
148 info.pWaitDstStageMask = &wait_stage;
149 info.commandBufferCount = 1;
150 info.pCommandBuffers = &fd->CommandBuffer;
151 info.signalSemaphoreCount = 1;
152 info.pSignalSemaphores = &render_complete_semaphore;
153
154 err = vkEndCommandBuffer(fd->CommandBuffer);
155 check_vk_result(err);
156 err = vkQueueSubmit(g_Queue, 1, &info, fd->Fence);
157 check_vk_result(err);
158 }
159}
160
161static void FramePresent(ImGui_ImplVulkanH_Window* wd)
162{
163 if (g_SwapChainRebuild)
164 return;
165 VkSemaphore render_complete_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].RenderCompleteSemaphore;
166 VkPresentInfoKHR info = {};
167 info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
168 info.waitSemaphoreCount = 1;
169 info.pWaitSemaphores = &render_complete_semaphore;
170 info.swapchainCount = 1;
171 info.pSwapchains = &wd->Swapchain;
172 info.pImageIndices = &wd->FrameIndex;
173 VkResult err = vkQueuePresentKHR(g_Queue, &info);
174 if (err == VK_ERROR_OUT_OF_DATE_KHR)
175 {
176 g_SwapChainRebuild = true;
177 return;
178 }
179 check_vk_result(err);
180 wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->ImageCount; // Now we can use the next set of semaphores
181}
182
183/********************************************* START OF NEW CODE *********************************************/
184
185VKAPI_ATTR VkBool32 VKAPI_CALL VulkanGame::debugCallback(
186 VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
187 VkDebugUtilsMessageTypeFlagsEXT messageType,
188 const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
189 void* pUserData) {
190 cerr << "validation layer: " << pCallbackData->pMessage << endl;
191
192 return VK_FALSE;
193}
194
195VulkanGame::VulkanGame(int maxFramesInFlight) : MAX_FRAMES_IN_FLIGHT(maxFramesInFlight) {
196 this->gui = nullptr;
197 this->window = nullptr;
198}
199
200VulkanGame::~VulkanGame() {
201}
202
203void VulkanGame::run(int width, int height, unsigned char guiFlags) {
204 cout << "DEBUGGING IS " << (ENABLE_VALIDATION_LAYERS ? "ON" : "OFF") << endl;
205
206 cout << "Vulkan Game" << endl;
207
208 if (initUI(width, height, guiFlags) == RTWO_ERROR) {
209 return;
210 }
211
212 initVulkan();
213
214 // Create Framebuffers
215 int w, h;
216 SDL_GetWindowSize(window, &w, &h);
217 ImGui_ImplVulkanH_Window* wd = &g_MainWindowData;
218 // TODO: SetupVulkanWIndow calls vkGetPhysicalDeviceSurfaceSupportKHR to get the present queue. In vulkan-game, I do this in findQueueFamilies.
219 SetupVulkanWindow(wd, surface, w, h);
220
221 // TODO: Start from here. I've already reviewed everything before this
222
223 // Setup Dear ImGui context
224 IMGUI_CHECKVERSION();
225 ImGui::CreateContext();
226 ImGuiIO& io = ImGui::GetIO(); (void)io;
227 //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
228 //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
229
230 // Setup Dear ImGui style
231 ImGui::StyleColorsDark();
232 //ImGui::StyleColorsClassic();
233
234 // Setup Platform/Renderer bindings
235 ImGui_ImplSDL2_InitForVulkan(window);
236 ImGui_ImplVulkan_InitInfo init_info = {};
237 init_info.Instance = g_Instance;
238 init_info.PhysicalDevice = g_PhysicalDevice;
239 init_info.Device = g_Device;
240 init_info.QueueFamily = g_QueueFamily;
241 init_info.Queue = g_Queue;
242 init_info.PipelineCache = g_PipelineCache;
243 init_info.DescriptorPool = g_DescriptorPool;
244 init_info.Allocator = g_Allocator;
245 init_info.MinImageCount = g_MinImageCount;
246 init_info.ImageCount = wd->ImageCount;
247 init_info.CheckVkResultFn = check_vk_result;
248 ImGui_ImplVulkan_Init(&init_info, wd->RenderPass);
249
250 // Load Fonts
251 // - 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.
252 // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
253 // - 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).
254 // - 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.
255 // - Read 'docs/FONTS.md' for more instructions and details.
256 // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
257 //io.Fonts->AddFontDefault();
258 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
259 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
260 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
261 //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f);
262 //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese());
263 //IM_ASSERT(font != NULL);
264
265 VkResult err;
266
267 // Upload Fonts
268 {
269 // Use any command queue
270 VkCommandPool command_pool = wd->Frames[wd->FrameIndex].CommandPool;
271 VkCommandBuffer command_buffer = wd->Frames[wd->FrameIndex].CommandBuffer;
272
273 err = vkResetCommandPool(g_Device, command_pool, 0);
274 check_vk_result(err);
275 VkCommandBufferBeginInfo begin_info = {};
276 begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
277 begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
278 err = vkBeginCommandBuffer(command_buffer, &begin_info);
279 check_vk_result(err);
280
281 ImGui_ImplVulkan_CreateFontsTexture(command_buffer);
282
283 VkSubmitInfo end_info = {};
284 end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
285 end_info.commandBufferCount = 1;
286 end_info.pCommandBuffers = &command_buffer;
287 err = vkEndCommandBuffer(command_buffer);
288 check_vk_result(err);
289 err = vkQueueSubmit(g_Queue, 1, &end_info, VK_NULL_HANDLE);
290 check_vk_result(err);
291
292 err = vkDeviceWaitIdle(g_Device);
293 check_vk_result(err);
294 ImGui_ImplVulkan_DestroyFontUploadObjects();
295 }
296
297 // Our state
298 bool show_demo_window = true;
299 bool show_another_window = false;
300 ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
301
302 // Main loop
303 bool done = false;
304 while (!done) {
305 // Poll and handle events (inputs, window resize, etc.)
306 // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
307 // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
308 // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
309 // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
310 SDL_Event event;
311 while (SDL_PollEvent(&event)) {
312 ImGui_ImplSDL2_ProcessEvent(&event);
313 if (event.type == SDL_QUIT)
314 done = true;
315 if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))
316 done = true;
317 }
318
319 // Resize swap chain?
320 if (g_SwapChainRebuild) {
321 int width, height;
322 SDL_GetWindowSize(window, &width, &height);
323 if (width > 0 && height > 0)
324 {
325 ImGui_ImplVulkan_SetMinImageCount(g_MinImageCount);
326 ImGui_ImplVulkanH_CreateOrResizeWindow(g_Instance, g_PhysicalDevice, g_Device, &g_MainWindowData, g_QueueFamily, g_Allocator, width, height, g_MinImageCount);
327 g_MainWindowData.FrameIndex = 0;
328 g_SwapChainRebuild = false;
329 }
330 }
331
332 // Start the Dear ImGui frame
333 ImGui_ImplVulkan_NewFrame();
334 ImGui_ImplSDL2_NewFrame(window);
335 ImGui::NewFrame();
336
337 // 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!).
338 if (show_demo_window)
339 ImGui::ShowDemoWindow(&show_demo_window);
340
341 // 2. Show a simple window that we create ourselves. We use a Begin/End pair to created a named window.
342 {
343 static float f = 0.0f;
344 static int counter = 0;
345
346 ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it.
347
348 ImGui::Text("This is some useful text."); // Display some text (you can use a format strings too)
349 ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state
350 ImGui::Checkbox("Another Window", &show_another_window);
351
352 ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
353 ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color
354
355 if (ImGui::Button("Button")) // Buttons return true when clicked (most widgets return true when edited/activated)
356 counter++;
357 ImGui::SameLine();
358 ImGui::Text("counter = %d", counter);
359
360 ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate);
361 ImGui::End();
362 }
363
364 // 3. Show another simple window.
365 if (show_another_window)
366 {
367 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)
368 ImGui::Text("Hello from another window!");
369 if (ImGui::Button("Close Me"))
370 show_another_window = false;
371 ImGui::End();
372 }
373
374 // Rendering
375 ImGui::Render();
376 ImDrawData* draw_data = ImGui::GetDrawData();
377 const bool is_minimized = (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f);
378 if (!is_minimized)
379 {
380 memcpy(&wd->ClearValue.color.float32[0], &clear_color, 4 * sizeof(float));
381 FrameRender(wd, draw_data);
382 FramePresent(wd);
383 }
384 }
385
386 // Cleanup
387 err = vkDeviceWaitIdle(g_Device);
388 check_vk_result(err);
389 ImGui_ImplVulkan_Shutdown();
390 ImGui_ImplSDL2_Shutdown();
391 ImGui::DestroyContext();
392
393 CleanupVulkanWindow();
394 cleanup();
395
396 close_log();
397}
398
399bool VulkanGame::initUI(int width, int height, unsigned char guiFlags) {
400 // TODO: Create a game-gui function to get the gui version and retrieve it that way
401
402 SDL_VERSION(&sdlVersion); // This gets the compile-time version
403 SDL_GetVersion(&sdlVersion); // This gets the runtime version
404
405 cout << "SDL " <<
406 to_string(sdlVersion.major) << "." <<
407 to_string(sdlVersion.minor) << "." <<
408 to_string(sdlVersion.patch) << endl;
409
410 // TODO: Refactor the logger api to be more flexible,
411 // esp. since gl_log() and gl_log_err() have issues printing anything besides strings
412 restart_gl_log();
413 gl_log("starting SDL\n%s.%s.%s",
414 to_string(sdlVersion.major).c_str(),
415 to_string(sdlVersion.minor).c_str(),
416 to_string(sdlVersion.patch).c_str());
417
418 // TODO: Use open_Log() and related functions instead of gl_log ones
419 // TODO: In addition, delete the gl_log functions
420 open_log();
421 get_log() << "starting SDL" << endl;
422 get_log() <<
423 (int)sdlVersion.major << "." <<
424 (int)sdlVersion.minor << "." <<
425 (int)sdlVersion.patch << endl;
426
427 // TODO: Put all fonts, textures, and images in the assets folder
428 gui = new GameGui_SDL();
429
430 if (gui->init() == RTWO_ERROR) {
431 // TODO: Also print these sorts of errors to the log
432 cout << "UI library could not be initialized!" << endl;
433 cout << gui->getError() << endl;
434 return RTWO_ERROR;
435 }
436
437 window = (SDL_Window*)gui->createWindow("Vulkan Game", width, height, guiFlags & GUI_FLAGS_WINDOW_FULLSCREEN);
438 if (window == nullptr) {
439 cout << "Window could not be created!" << endl;
440 cout << gui->getError() << endl;
441 return RTWO_ERROR;
442 }
443
444 cout << "Target window size: (" << width << ", " << height << ")" << endl;
445 cout << "Actual window size: (" << gui->getWindowWidth() << ", " << gui->getWindowHeight() << ")" << endl;
446
447 return RTWO_SUCCESS;
448}
449
450void VulkanGame::initVulkan() {
451 const vector<const char*> validationLayers = {
452 "VK_LAYER_KHRONOS_validation"
453 };
454 const vector<const char*> deviceExtensions = {
455 VK_KHR_SWAPCHAIN_EXTENSION_NAME
456 };
457
458 createVulkanInstance(validationLayers);
459 setupDebugMessenger();
460 createVulkanSurface();
461 pickPhysicalDevice(deviceExtensions);
462 createLogicalDevice(validationLayers, deviceExtensions);
463
464 VkResult err;
465
466 // Create Descriptor Pool
467 {
468 VkDescriptorPoolSize pool_sizes[] =
469 {
470 { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 },
471 { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 },
472 { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 },
473 { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 },
474 { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 },
475 { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 },
476 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 },
477 { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 },
478 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 },
479 { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 },
480 { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 }
481 };
482 VkDescriptorPoolCreateInfo pool_info = {};
483 pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
484 pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
485 pool_info.maxSets = 1000 * IM_ARRAYSIZE(pool_sizes);
486 pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes);
487 pool_info.pPoolSizes = pool_sizes;
488 err = vkCreateDescriptorPool(g_Device, &pool_info, g_Allocator, &g_DescriptorPool);
489 check_vk_result(err);
490 }
491}
492
493void VulkanGame::cleanup() {
494 vkDestroyDescriptorPool(g_Device, g_DescriptorPool, g_Allocator);
495
496 if (ENABLE_VALIDATION_LAYERS) {
497 VulkanUtils::destroyDebugUtilsMessengerEXT(g_Instance, debugMessenger, nullptr);
498 }
499
500 vkDestroyDevice(g_Device, g_Allocator);
501 vkDestroyInstance(g_Instance, g_Allocator);
502
503 gui->destroyWindow();
504 gui->shutdown();
505 delete gui;
506}
507
508void VulkanGame::createVulkanInstance(const vector<const char*>& validationLayers) {
509 if (ENABLE_VALIDATION_LAYERS && !VulkanUtils::checkValidationLayerSupport(validationLayers)) {
510 throw runtime_error("validation layers requested, but not available!");
511 }
512
513 VkApplicationInfo appInfo = {};
514 appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
515 appInfo.pApplicationName = "Vulkan Game";
516 appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
517 appInfo.pEngineName = "No Engine";
518 appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
519 appInfo.apiVersion = VK_API_VERSION_1_0;
520
521 VkInstanceCreateInfo createInfo = {};
522 createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
523 createInfo.pApplicationInfo = &appInfo;
524
525 vector<const char*> extensions = gui->getRequiredExtensions();
526 if (ENABLE_VALIDATION_LAYERS) {
527 extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
528 }
529
530 createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
531 createInfo.ppEnabledExtensionNames = extensions.data();
532
533 cout << endl << "Extensions:" << endl;
534 for (const char* extensionName : extensions) {
535 cout << extensionName << endl;
536 }
537 cout << endl;
538
539 VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo;
540 if (ENABLE_VALIDATION_LAYERS) {
541 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
542 createInfo.ppEnabledLayerNames = validationLayers.data();
543
544 populateDebugMessengerCreateInfo(debugCreateInfo);
545 createInfo.pNext = &debugCreateInfo;
546 } else {
547 createInfo.enabledLayerCount = 0;
548
549 createInfo.pNext = nullptr;
550 }
551
552 if (vkCreateInstance(&createInfo, nullptr, &g_Instance) != VK_SUCCESS) {
553 throw runtime_error("failed to create instance!");
554 }
555}
556
557void VulkanGame::setupDebugMessenger() {
558 if (!ENABLE_VALIDATION_LAYERS) {
559 return;
560 }
561
562 VkDebugUtilsMessengerCreateInfoEXT createInfo;
563 populateDebugMessengerCreateInfo(createInfo);
564
565 if (VulkanUtils::createDebugUtilsMessengerEXT(g_Instance, &createInfo, nullptr, &debugMessenger) != VK_SUCCESS) {
566 throw runtime_error("failed to set up debug messenger!");
567 }
568}
569
570void VulkanGame::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
571 createInfo = {};
572 createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
573 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;
574 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;
575 createInfo.pfnUserCallback = debugCallback;
576}
577
578void VulkanGame::createVulkanSurface() {
579 if (gui->createVulkanSurface(g_Instance, &surface) == RTWO_ERROR) {
580 throw runtime_error("failed to create window surface!");
581 }
582}
583
584void VulkanGame::pickPhysicalDevice(const vector<const char*>& deviceExtensions) {
585 uint32_t deviceCount = 0;
586 // TODO: Check VkResult
587 vkEnumeratePhysicalDevices(g_Instance, &deviceCount, nullptr);
588
589 if (deviceCount == 0) {
590 throw runtime_error("failed to find GPUs with Vulkan support!");
591 }
592
593 vector<VkPhysicalDevice> devices(deviceCount);
594 // TODO: Check VkResult
595 vkEnumeratePhysicalDevices(g_Instance, &deviceCount, devices.data());
596
597 cout << endl << "Graphics cards:" << endl;
598 for (const VkPhysicalDevice& device : devices) {
599 if (isDeviceSuitable(device, deviceExtensions)) {
600 g_PhysicalDevice = device;
601 break;
602 }
603 }
604 cout << endl;
605
606 if (g_PhysicalDevice == VK_NULL_HANDLE) {
607 throw runtime_error("failed to find a suitable GPU!");
608 }
609}
610
611bool VulkanGame::isDeviceSuitable(VkPhysicalDevice physicalDevice, const vector<const char*>& deviceExtensions) {
612 VkPhysicalDeviceProperties deviceProperties;
613 vkGetPhysicalDeviceProperties(physicalDevice, &deviceProperties);
614
615 cout << "Device: " << deviceProperties.deviceName << endl;
616
617 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(physicalDevice, surface);
618 bool extensionsSupported = VulkanUtils::checkDeviceExtensionSupport(physicalDevice, deviceExtensions);
619 bool swapChainAdequate = false;
620
621 if (extensionsSupported) {
622 SwapChainSupportDetails swapChainSupport = VulkanUtils::querySwapChainSupport(physicalDevice, surface);
623 swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty();
624 }
625
626 VkPhysicalDeviceFeatures supportedFeatures;
627 vkGetPhysicalDeviceFeatures(physicalDevice, &supportedFeatures);
628
629 return indices.isComplete() && extensionsSupported && swapChainAdequate && supportedFeatures.samplerAnisotropy;
630}
631
632void VulkanGame::createLogicalDevice(const vector<const char*>& validationLayers,
633 const vector<const char*>& deviceExtensions) {
634 QueueFamilyIndices indices = VulkanUtils::findQueueFamilies(g_PhysicalDevice, surface);
635 g_QueueFamily = indices.graphicsFamily.value();
636
637 vector<VkDeviceQueueCreateInfo> queueCreateInfoList;
638 set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value() };
639
640 float queuePriority = 1.0f;
641 for (uint32_t queueFamily : uniqueQueueFamilies) {
642 VkDeviceQueueCreateInfo queueCreateInfo = {};
643 queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
644 queueCreateInfo.queueCount = 1;
645 queueCreateInfo.queueFamilyIndex = queueFamily;
646 queueCreateInfo.pQueuePriorities = &queuePriority;
647
648 queueCreateInfoList.push_back(queueCreateInfo);
649 }
650
651 VkPhysicalDeviceFeatures deviceFeatures = {};
652 deviceFeatures.samplerAnisotropy = VK_TRUE;
653
654 VkDeviceCreateInfo createInfo = {};
655 createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
656
657 createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfoList.size());
658 createInfo.pQueueCreateInfos = queueCreateInfoList.data();
659
660 createInfo.pEnabledFeatures = &deviceFeatures;
661
662 createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
663 createInfo.ppEnabledExtensionNames = deviceExtensions.data();
664
665 // These fields are ignored by up-to-date Vulkan implementations,
666 // but it's a good idea to set them for backwards compatibility
667 if (ENABLE_VALIDATION_LAYERS) {
668 createInfo.enabledLayerCount = static_cast<uint32_t>(validationLayers.size());
669 createInfo.ppEnabledLayerNames = validationLayers.data();
670 }
671 else {
672 createInfo.enabledLayerCount = 0;
673 }
674
675 if (vkCreateDevice(g_PhysicalDevice, &createInfo, nullptr, &g_Device) != VK_SUCCESS) {
676 throw runtime_error("failed to create logical device!");
677 }
678
679 vkGetDeviceQueue(g_Device, g_QueueFamily, 0, &g_Queue);
680 //vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
681 //vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
682}
683
684/********************************************** END OF NEW CODE **********************************************/
Note: See TracBrowser for help on using the repository browser.