diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e6d672..33f3b93 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,7 +89,7 @@ else() endif() if (DEFINED TINY_OBJ_PATH) - message(STATUS "Using TINY_OBJ_PATH frm .env.cmake: ${TINY_OBJ_PATH}") + message(STATUS "Using TINY_OBJ_PATH from .env.cmake: ${TINY_OBJ_PATH}") set(TINY_OBJ_INCLUDE_DIR "${TINY_OBJ_PATH}") add_library(tiny_obj INTERFACE) @@ -98,6 +98,21 @@ else() message(FATAL_ERROR "TINY_OBJ_PATH is not defined in .env.cmake!") endif() +if (DEFINED IMGUI_PATH) + # Creates a library from the basic ImGui files in the location provided in .env.cmake + message(STATUS "Using IMGUI_PATH from .env.cmake: ${IMGUI_PATH}") + file(GLOB IMGUI_SRC ${IMGUI_PATH}/*.cpp ${IMGUI_PATH}/*.h) + add_library(imgui ${IMGUI_SRC}) + # Also creates a library for the needed backends for this project and links Vulkan and GLFW + file(GLOB IMGUI_BACKEND_SRC ${IMGUI_PATH}/backends/imgui_impl_glfw.* + ${IMGUI_PATH}/backends/imgui_impl_vulkan.*) + add_library(imgui_backends ${IMGUI_BACKEND_SRC}) + target_include_directories(imgui_backends PUBLIC ${Vulkan_INCLUDE_DIRS} ${GLFW_INCLUDE_DIRS} ${IMGUI_PATH}) + target_link_libraries(imgui_backends PUBLIC imgui glfw3 vulkan-1) +else() + message(FATAL_ERROR "IMGUI_PATH is not defined in .env.cmake!") +endif() + # Gathers all .cpp files in the source directory and all subdirectories and compiles hem to an executable using C++ 17 file(GLOB_RECURSE SOURCES ${PROJECT_SOURCE_DIR}/source/*.cpp) add_executable(${PROJECT_NAME} ${SOURCES}) @@ -128,6 +143,8 @@ if (WIN32) ${GLM_PATH} ${STB_IMAGE_INCLUDE_DIR} ${TINY_OBJ_INCLUDE_DIR} + ${IMGUI_PATH} + ${IMGUI_PATH}/backends ) # Setting Up Linker Directories for compilation @@ -137,7 +154,7 @@ if (WIN32) ) # Linking required libraries - target_link_libraries(${PROJECT_NAME} glfw3 vulkan-1 stb_image tiny_obj) + target_link_libraries(${PROJECT_NAME} glfw3 vulkan-1 stb_image tiny_obj imgui imgui_backends) # If use is on Linux elseif (UNIX) # Create a build for Linux @@ -149,7 +166,7 @@ elseif (UNIX) ) # Linking required libraries - target_link_libraries(${PROJECT_NAME} glfw ${Vulkan_LIBRARIES} stb_image tiny_obj) + target_link_libraries(${PROJECT_NAME} glfw ${Vulkan_LIBRARIES} stb_image tiny_obj imgui imgui_backends) endif() ##### For Compiling Shader Objects ##### diff --git a/docs/milestone-documentation-Caleb/vkQueueSubmit-upgrade-attempt.md b/docs/milestone-documentation-Caleb/vkQueueSubmit-upgrade-attempt.md new file mode 100644 index 0000000..f89bdf2 --- /dev/null +++ b/docs/milestone-documentation-Caleb/vkQueueSubmit-upgrade-attempt.md @@ -0,0 +1,49 @@ +Attempting to fix the Nvidia GPU bug, I tried to upgrade the command buffer submission process to use `vkQueueSubmit2()` rather than `vkQueueSubmit()`. This attempt was unsuccessful and only causes the program to crash since I believe `vkQueueSubmit2()` requires functionality that the game engine does not currently possess. I also don't think my upgrade would've fixed the Nvidia bug anyways due to finding another issue that often occurs. However, I thought I would add my code here on the off chance that the game engine can use it in the future so that it could be easily readded. This code would go above the `vkQueueSubmit()` function call (which should be replaced by the last line of this code snippet) in [swapchain.cpp](../../source/engine/src/swapChain.cpp) which is located on line 173 at the time of writing this. + +```cpp +const int waitArrayLength = sizeof(waitSemaphores)/sizeof(VkSemaphore); +VkSemaphoreSubmitInfo waitSemaphores2[waitArrayLength]; +VkPipelineStageFlags2 waitStages2[] = { VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT }; +for(int i = 0; i < waitArrayLength; ++i) { + waitSemaphores2[i].sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO; + waitSemaphores2[i].pNext = nullptr; + waitSemaphores2[i].semaphore = waitSemaphores[i]; + waitSemaphores2[i].stageMask = waitStages2[i]; + waitSemaphores2[i].deviceIndex = 0; + waitSemaphores2[i].value = 1; +} + +const int bufferArrayLength = sizeof(buffers)/sizeof(VkCommandBuffer); +VkCommandBufferSubmitInfo buffers2[1]; +for(int i = 0; i < bufferArrayLength; ++i) { + buffers2[i].sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO; + buffers2[i].pNext = nullptr; + buffers2[i].commandBuffer = buffers[i]; + buffers2[i].deviceMask = 0; +} + +const int signalArrayLength = sizeof(signalSemaphores)/sizeof(VkSemaphore); +VkSemaphoreSubmitInfo signalSemaphores2[1]; +VkPipelineStageFlags2 signalStages2[] = { VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT }; +for(int i = 0; i < signalArrayLength; ++i) { + waitSemaphores2[i].sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO; + waitSemaphores2[i].pNext = nullptr; + waitSemaphores2[i].semaphore = signalSemaphores[i]; + waitSemaphores2[i].stageMask = signalStages2[i]; + waitSemaphores2[i].deviceIndex = 0; + waitSemaphores2[i].value = 1; +} +VkSubmitInfo2 submitInfo2 = {}; +submitInfo2.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2; +submitInfo2.pNext = nullptr; +submitInfo2.waitSemaphoreInfoCount = 1; +submitInfo2.pWaitSemaphoreInfos = waitSemaphores2; +submitInfo2.signalSemaphoreInfoCount = 1; +submitInfo2.pSignalSemaphoreInfos = signalSemaphores2; +submitInfo2.commandBufferInfoCount = 1; +submitInfo2.pCommandBufferInfos = buffers2; + +//vkResetFences function is called here + +if (vkQueueSubmit2(device.graphicsQueue(), 1, &submitInfo2, inFlightFences[currentFrame] != VK_SUCCESS)) { +``` \ No newline at end of file diff --git a/docs/project-setup/LinuxSetup.md b/docs/project-setup/LinuxSetup.md index 694c354..4c32fac 100644 --- a/docs/project-setup/LinuxSetup.md +++ b/docs/project-setup/LinuxSetup.md @@ -21,10 +21,13 @@ To edit and run the project on a computer using Linux, follow these steps: - Put the directory containing these files in a known location 7. Download the stb_image.h file located [here](https://github.com/nothings/stb/blob/master/stb_image.h) and store it in a known location 8. Download the tiny_obj_loader.h file located [here](https://github.com/tinyobjloader/tinyobjloader/blob/release/tiny_obj_loader.h) and store it in a known location -9. Go to the directory where you cloned the GitHub Repository to (perhaps named JCAT Game Engine) and make a copy of the envUnixTemplate.cmake file and name it ".env.cmake" (in Visual Studio Code, the file icon may change to a green dollar sign) -10. Change the file paths in this .env.cmake file to be the locations of the previously downloaded files and directories +9. Download the latest .zip file from [here](https://github.com/ocornut/imgui/releases) (scroll down until you find the Assets section) + - Extract the folder within the .zip file (name should be imgui followed by a version number) and put it in a known location + - Do not change the folder structure of this folder, but if you want to avoid clutter, you can remove all of the files from the backends folder **except for those with glfw or vulkan in the file name** +10. Go to the directory where you cloned the GitHub Repository to (perhaps named JCAT Game Engine) and make a copy of the envUnixTemplate.cmake file and name it ".env.cmake" (in Visual Studio Code, the file icon may change to a green dollar sign) +11. Change the file paths in this .env.cmake file to be the locations of the previously downloaded files and directories - Follow the instructions in the comments and the example file paths ("/path/to/your...") -11. To run the game engine application, go to the directory where you cloned the repository, select unixBuild (a .sh file) and when that finishes running, go to the "build" directory and finally select the application file named "JCATEngine.exe" +12. To run the game engine application, go to the directory where you cloned the repository, select unixBuild (a .sh file) and when that finishes running, go to the "build" directory and finally select the application file named "JCATEngine.exe" - The application is running correctly if a new window named "JCAT Game Engine" shows a non-blank screen - If your computer has an Nvidia GPU and either is plugged in or has no other GPU, the game engine may crash on start up. If this happens, keep running the application and it should eventually work. \ No newline at end of file diff --git a/docs/project-setup/MacSetup.md b/docs/project-setup/MacSetup.md index b54a0c0..e494517 100644 --- a/docs/project-setup/MacSetup.md +++ b/docs/project-setup/MacSetup.md @@ -21,10 +21,13 @@ To edit and run the project on a computer using macOS, follow these steps: - Extract the directory within the .zip file (named glfw-3.4.bin.WIN64) and put it in a known location 7. Download the stb_image.h file located [here](https://github.com/nothings/stb/blob/master/stb_image.h) and store it in a known location 8. Download the tiny_obj_loader.h file located [here](https://github.com/tinyobjloader/tinyobjloader/blob/release/tiny_obj_loader.h) and store it in a known location -9. Go to the directory where you cloned the GitHub Repository to (perhaps named JCAT Game Engine) and make a copy of the envUnixTemplate.cmake file and name it ".env.cmake" (in Visual Studio Code, the file icon may change to a green dollar sign) -10. Change the file paths in this .env.cmake file to be the locations of the previously downloaded files and directories +9. Download the latest .zip file from [here](https://github.com/ocornut/imgui/releases) (scroll down until you find the Assets section) + - Extract the folder within the .zip file (name should be imgui followed by a version number) and put it in a known location + - Do not change the folder structure of this folder, but if you want to avoid clutter, you can remove all of the files from the backends folder **except for those with glfw or vulkan in the file name** +10. Go to the directory where you cloned the GitHub Repository to (perhaps named JCAT Game Engine) and make a copy of the envUnixTemplate.cmake file and name it ".env.cmake" (in Visual Studio Code, the file icon may change to a green dollar sign) +11. Change the file paths in this .env.cmake file to be the locations of the previously downloaded files and directories - Follow the instructions in the comments and the example file paths ("/path/to/your...") -11. To run the game engine application, go to the directory where you cloned the repository, select unixBuild (a .sh file) and when that finishes running, go to the "build" directory and finally select the application file named "JCATEngine.exe" +12. To run the game engine application, go to the directory where you cloned the repository, select unixBuild (a .sh file) and when that finishes running, go to the "build" directory and finally select the application file named "JCATEngine.exe" - The application is running correctly if a new window named "JCAT Game Engine" shows a non-blank screen - If your computer has an Nvidia GPU and either is plugged in or has no other GPU, the game engine may crash on start up. If this happens, keep running the application and it should eventually work. \ No newline at end of file diff --git a/docs/project-setup/WindowsSetup.md b/docs/project-setup/WindowsSetup.md index 7e395fb..c755b0a 100644 --- a/docs/project-setup/WindowsSetup.md +++ b/docs/project-setup/WindowsSetup.md @@ -26,16 +26,19 @@ To edit and run the project on a Windows computer, follow these steps: - Extract the folder within the .zip file (named glfw-3.4.bin.WIN64) and put it in a known location 8. Download the stb_image.h file located [here](https://github.com/nothings/stb/blob/master/stb_image.h) and store it in a known location 9. Download the tiny_obj_loader.h file located [here](https://github.com/tinyobjloader/tinyobjloader/blob/release/tiny_obj_loader.h) and store it in a known location -10. Go to the folder where you cloned the GitHub Repository to (perhaps named JCAT Game Engine) and make a copy of the envWindowsTemplate.cmake file and name it ".env.cmake" (in Visual Studio Code, the file icon may change to a green dollar sign) -11. Change the file paths in this .env.cmake file to be the locations of the previously downloaded files and folders +10. Download the latest .zip file from [here](https://github.com/ocornut/imgui/releases) (scroll down until you find the Assets section) + - Extract the folder within the .zip file (name should be imgui followed by a version number) and put it in a known location + - Do not change the folder structure of this folder, but if you want to avoid clutter, you can remove all of the files from the backends folder **except for those with glfw or vulkan in the file name** +11. Go to the folder where you cloned the GitHub Repository to (perhaps named JCAT Game Engine) and make a copy of the envWindowsTemplate.cmake file and name it ".env.cmake" (in Visual Studio Code, the file icon may change to a green dollar sign) +12. Change the file paths in this .env.cmake file to be the locations of the previously downloaded files and folders - Follow the instructions in the comments and the example file paths ("Path/To/Your...") - If you used the default location for Vulkan installation, your VULKAN_SDK_PATH will be "C:/VulkanSDK/1.3.290.0" -12. Add the mingw bin folder to your Path Environment Variable +13. Add the mingw bin folder to your Path Environment Variable - Find "Edit the system environment variables" by searching for it in your Windows search bar. - In the window that pops up, click the button that says "Environment Variables" at the bottom - In the new pop-up window, click "Path" and then the "Edit" button - Finally, click "New" and then paste the file path to your mingw folder's bin folder (".../mingw/bin") -13. To run the game engine application, go to the folder where you cloned the repository , select mingwBuild (a .bat file) and when that finishes running, go to the "build" folder and finally select the application file named "JCATEngine.exe" +14. To run the game engine application, go to the folder where you cloned the repository , select mingwBuild (a .bat file) and when that finishes running, go to the "build" folder and finally select the application file named "JCATEngine.exe" - The application is running correctly if a new window named "JCAT Game Engine" shows a non-blank screen - If your computer has an Nvidia GPU and either is plugged in or has no other GPU, the game engine may crash on start up. If this happens, keep running the application and it should eventually work. \ No newline at end of file diff --git a/envUnixTemplate.cmake b/envUnixTemplate.cmake index e7a092d..df8fa82 100644 --- a/envUnixTemplate.cmake +++ b/envUnixTemplate.cmake @@ -7,3 +7,4 @@ set(GLM_PATH "/path/to/your/GLM/folder/named/glm") set(VULKAN_SDK_PATH "/path/to/your/Vulkan/version/folder/for/example/1.3.290.0") set(STB_PATH "C:/path/to/your/stb/library/folder/for/example/stb") set(TINY_OBJ_PATH "C:/path/to/your/tiny/obj/folder/for/example/tinyobjloader") +set(IMGUI_PATH "C:/path/to/your/imgui/folder/containing/c++/files/named/imgui") diff --git a/envWindowsTemplate.cmake b/envWindowsTemplate.cmake index 5b4cc8a..0c9b429 100644 --- a/envWindowsTemplate.cmake +++ b/envWindowsTemplate.cmake @@ -7,6 +7,7 @@ set(GLM_PATH "Path/To/Your/GLM/Folder/Named/glm") set(VULKAN_SDK_PATH "Path/To/Your/Vulkan/Version/Folder/For/Example/1.3.290.0") set(STB_PATH "C:/path.to.your/stb/library/folder/for/example/stb") set(TINY_OBJ_PATH "C:/path/to/your/tiny/obj/folder/for/example/tinyobjloader") +set(IMGUI_PATH "C:/path/to/your/imgui/folder/containing/c++/files/named/imgui") # Set this path if you are NOT using Visual Studio set(MINGW_PATH "Path/To/Your/mingw/Folder/Named/mingw64") \ No newline at end of file diff --git a/source/appCore/imguiHandler.cpp b/source/appCore/imguiHandler.cpp new file mode 100644 index 0000000..9d786b7 --- /dev/null +++ b/source/appCore/imguiHandler.cpp @@ -0,0 +1,64 @@ +#include "./appCore/imguiHandler.h" + +namespace JCAT { + + ImGuiIO& ImGuiHandler::initializeImGui(Window &window, DeviceSetup &device, Renderer &renderer, bool lightStyle) { + // Create ImGui context and set up IO functions + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls + + // Setup ImGui style + if(lightStyle) ImGui::StyleColorsLight(); + else ImGui::StyleColorsDark(); + + // ImGui setup for working with Vulkan + ImGui_ImplGlfw_InitForVulkan(window.getWindow(), false); + ImGui_ImplVulkan_InitInfo initInfo {}; + initInfo.Instance = device.getInstance(); + initInfo.PhysicalDevice = device.getPhysicalDevice(); + initInfo.Device = device.device(); + initInfo.QueueFamily = device.findPhysicalQueueFamilies().graphicsFamily; + initInfo.Queue = device.graphicsQueue(); + initInfo.PipelineCache = VK_NULL_HANDLE; + initInfo.DescriptorPoolSize = 3; + initInfo.RenderPass = renderer.getSwapChainrenderPass(); + initInfo.Subpass = 0; + initInfo.MinImageCount = 2; + initInfo.ImageCount = 2; + initInfo.MSAASamples = VK_SAMPLE_COUNT_1_BIT; + initInfo.Allocator = nullptr; + initInfo.CheckVkResultFn = check_vk_result; + ImGui_ImplVulkan_Init(&initInfo); + + return io; + } + + void ImGuiHandler::startImGuiFrame() { + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + } + + void ImGuiHandler::renderImGui(VkCommandBuffer &commandBuffer) { + // Records ImGui primitives to given command buffer + ImGui::Render(); + ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), commandBuffer); + } + + void ImGuiHandler::shutdownImGui() { + ImGui_ImplVulkan_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); + } + + void ImGuiHandler::check_vk_result(VkResult err) { + if (err == VK_SUCCESS) + return; + fprintf(stderr, "[vulkan] Error: VkResult = %d\n", err); + if (err < 0) + abort(); + } +}; //JCAT \ No newline at end of file diff --git a/source/appCore/imguiHandler.h b/source/appCore/imguiHandler.h new file mode 100644 index 0000000..92892a8 --- /dev/null +++ b/source/appCore/imguiHandler.h @@ -0,0 +1,59 @@ +#ifndef IMGUI_HANDLER +#define IMGUI_HANDLER + +#include "./engine/window.h" +#include "./engine/deviceSetup.h" +#include "./engine/renderer.h" + +#include "imgui.h" +#include "imgui_impl_glfw.h" +#include "imgui_impl_vulkan.h" + +namespace JCAT { + /** + * @class ImGuiHandler + * @brief This class contains static functions for simplifying working with ImGui and its + * GLFW and Vulkan implementations to implement a UI interface to thIS game engine + * + * @details The Dear ImGui Open Source Library (https://github.com/ocornut/imgui) provides a lot + * of functionality for implementing UI (including text, sliders, buttons, and more input options) + * into a preexisting application. If working with ImGui here, you may want to read the comments + * in imgui.h, imgui_impl_vulkan.h, and maybe imgui_impl_glfw.h (all in the repository) beforehand. + * + * https://github.com/ocornut/imgui/tree/master/examples/example_glfw_vulkan + * This example from the repository was used to help implement many of this features in this class + */ + class ImGuiHandler { + public: + /** + * Creates ImGui context & IO configuration and gets ImGui ready to work with Vulkan + * @param window JCAT Window object being used by current application + * @param device JCAT DeviceSetup object being used by current application + * @param renderer JCAT Renderer object being used by current application + * @param lightStyle Whether to use a "light mode" style in ImGui UI window(s) + * @return Reference to ImGui IO configuration object to be used by application + */ + static ImGuiIO& initializeImGui(Window &window, DeviceSetup &device, Renderer &renderer, bool lightStyle = false); + + /// Creates a new ImGui frame for GLFW and Vulkan + static void startImGuiFrame(); + + /** + * Renders ImGui content and records ImGui primitives to the given command buffer + * @param commandBuffer Vulkan command buffer to add ImGui content to + */ + static void renderImGui(VkCommandBuffer &commandBuffer); + + /// Shuts down ImGui processes for GLFW and Vulkan and destroys ImGui context + static void shutdownImGui(); + + /** + * Vulkan result checker to be used by ImGui Vulkan implementation, + * If err is not VK_SUCCESS, Prints error message to stderr and aborts application + * @param VkResult Vulkan result to be checked + */ + static void check_vk_result(VkResult err); + }; +}; //JCAT + +#endif \ No newline at end of file diff --git a/source/apps/default/2d/application.cpp b/source/apps/default/2d/application.cpp index c8957b7..987e5b8 100644 --- a/source/apps/default/2d/application.cpp +++ b/source/apps/default/2d/application.cpp @@ -1,6 +1,7 @@ #include "./apps/default/2d/application.h" #include "./appCore/keyboardController.h" +#include "./appCore/imguiHandler.h" #include "./engine/2d/camera2D.h" #include "./apps/default/2d/applicationRenderer.h" @@ -33,6 +34,8 @@ namespace JCAT { std::chrono::time_point currentTime = std::chrono::high_resolution_clock::now(); + ImGuiIO &io = ImGuiHandler::initializeImGui(window, device, renderer, true); + while (!window.shouldWindowClose()) { glfwPollEvents(); @@ -51,15 +54,32 @@ namespace JCAT { float aspect = renderer.getAspectRatio(); camera.setOrthographicProjection(-aspect, aspect, -1, 1); + // Start the ImGui frame + ImGuiHandler::startImGuiFrame(); + + // Show a basic window with some FPS info + ImGui::Begin("Application Frame Rate"); + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + ImGui::End(); + if (VkCommandBuffer commandBuffer = renderer.beginRecordingFrame()) { + // Begin render pass and render game sprites renderer.beginSwapChainRenderPass(commandBuffer); applicationRenderer.renderGameObjects(commandBuffer, gameSprites, camera); + + // Render ImGui content and record ImGui primitives into command buffer + // Last part of the process so UI windows are always at front + ImGuiHandler::renderImGui(commandBuffer); + + // End render pass and recording and then submit command buffers renderer.endSwapChainRenderPass(commandBuffer); renderer.endRecordingFrame(); } } + // Waits for queue operations to complete and shuts down ImGui vkDeviceWaitIdle(device.device()); + ImGuiHandler::shutdownImGui(); } void Application::loadGameSprites() { diff --git a/source/apps/default/2d/applicationRenderer.h b/source/apps/default/2d/applicationRenderer.h index 22069ca..8f63d5e 100644 --- a/source/apps/default/2d/applicationRenderer.h +++ b/source/apps/default/2d/applicationRenderer.h @@ -13,15 +13,37 @@ namespace JCAT { class ApplicationRenderer { public: + /** + * Constructs a new 2D Application Renderer object using the given arguments + * @param d JCAT DeviceSetup object for new application renderer object to use + * @param r JCAT ResourceManager object for the new application render object to use + * @param renderPass Vulkan render pass to manage frame buffer and attachment usage + * @param globalSetLayout The descriptor set layout handle to use in creating pipeline layout + */ ApplicationRenderer(DeviceSetup& d, ResourceManager& r, VkRenderPass renderPass); + + // Destroys the Vulkan pipeline layout being used by application ~ApplicationRenderer(); + // Make sure an instance of this class cannot be copied ApplicationRenderer(const ApplicationRenderer&) = delete; ApplicationRenderer& operator=(const ApplicationRenderer&) = delete; + /** + * Renders the given game sprites into the command buffer of the given FrameInfo + * @param commandBuffer The Vulkan command buffer to render the given sprites into + * @param gameSprites The JCAT game sprites to render into the command buffer + * @param camera Reference to the JCAT 2D Camera object to obtain current projection view from + */ void renderGameObjects(VkCommandBuffer commandBuffer, std::vector& gameSprites, const Camera2D& camera); private: + // Creates the pipeline layout to be used by the 2D Application Renderer object void createPipelineLayout(); + + /** + * Creates a new solid sprite graphics pipeline with a unique C++ pointer + * @param renderPass Vulkan render pass to manage frame buffer and attachment usage + */ void createPipeline(VkRenderPass renderPass); DeviceSetup& device; diff --git a/source/apps/default/3d/application3D.cpp b/source/apps/default/3d/application3D.cpp index c922b8c..621e8d7 100644 --- a/source/apps/default/3d/application3D.cpp +++ b/source/apps/default/3d/application3D.cpp @@ -1,6 +1,7 @@ #include "./apps/default/3d/application3D.h" #include "./appCore/keyboardController.h" +#include "./appCore/imguiHandler.h" #include "./engine/3d/camera3D.h" #include "./apps/default/3d/application3DRenderer.h" #include "./apps/default/3d/perlinNoise3D.h" @@ -95,6 +96,8 @@ namespace JCAT { std::chrono::time_point currentTime = std::chrono::high_resolution_clock::now(); + ImGuiIO &io = ImGuiHandler::initializeImGui(window, device, renderer); + while (!window.shouldWindowClose()) { glfwPollEvents(); @@ -108,6 +111,14 @@ namespace JCAT { float aspect = renderer.getAspectRatio(); camera.setPerspectiveProjection(glm::radians(50.f), aspect, 0.1f, 100.f); + // Start the ImGui frame + ImGuiHandler::startImGuiFrame(); + + // Show a basic window with some FPS info + ImGui::Begin("Application Frame Rate"); + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + ImGui::End(); + if (VkCommandBuffer commandBuffer = renderer.beginRecordingFrame()) { // Create new FrameInfo object that stores relevant frame information @@ -126,15 +137,23 @@ namespace JCAT { uboBuffers[frameIndex]->writeToBuffer(&ubo); uboBuffers[frameIndex]->flush(); - // render + // Begin render pass and render game objects renderer.beginSwapChainRenderPass(commandBuffer); applicationRenderer.renderGameObjects(frameInfo, gameObjects); + + // Render ImGui content and record ImGui primitives into command buffer + // Last part of the process so UI windows are always at front + ImGuiHandler::renderImGui(commandBuffer); + + // End render pass and recording and then submit command buffers renderer.endSwapChainRenderPass(commandBuffer); renderer.endRecordingFrame(); } } + // Waits for queue operations to complete and shuts down ImGui vkDeviceWaitIdle(device.device()); + ImGuiHandler::shutdownImGui(); } // Courtesy of tutorial for this: diff --git a/source/apps/default/3d/application3DRenderer.h b/source/apps/default/3d/application3DRenderer.h index 07c8d04..653021b 100644 --- a/source/apps/default/3d/application3DRenderer.h +++ b/source/apps/default/3d/application3DRenderer.h @@ -14,15 +14,39 @@ namespace JCAT { class Application3DRenderer { public: + /** + * Constructs a new 3D Application Renderer object using the given arguments + * @param d JCAT DeviceSetup object for new application renderer object to use + * @param r JCAT ResourceManager object for the new application render object to use + * @param renderPass Vulkan render pass to manage frame buffer and attachment usage + * @param globalSetLayout The descriptor set layout handle to use in creating pipeline layout + */ Application3DRenderer(DeviceSetup& d, ResourceManager& r, VkRenderPass renderPass, VkDescriptorSetLayout globalSetLayout); + + // Destroys the Vulkan pipeline layout being used by application ~Application3DRenderer(); + // Make sure an instance of this class cannot be copied Application3DRenderer(const Application3DRenderer&) = delete; Application3DRenderer& operator=(const Application3DRenderer&) = delete; + /** + * Renders the given game objects into the command buffer of the given FrameInfo + * @param frameInfo Reference to the JCAT FrameInfo struct to use in rendering game objects + * @param gameObjects The JCAT game objects to render into the command buffer + */ void renderGameObjects(FrameInfo &frameInfo, std::vector& gameObjects); private: + /** + * Creates the pipeline layout to be used by the 3D Application Renderer object + * @param globalSetLayout The descriptor set layout handle to use in creating pipeline layout + */ void createPipelineLayout(VkDescriptorSetLayout globalSetLayout); + + /** + * Creates a new solid object graphics pipeline with a unique C++ pointer + * @param renderPass Vulkan render pass to manage frame buffer and attachment usage + */ void createPipeline(VkRenderPass renderPass); DeviceSetup& device; diff --git a/source/engine/deviceSetup.h b/source/engine/deviceSetup.h index e854b00..b5cb191 100644 --- a/source/engine/deviceSetup.h +++ b/source/engine/deviceSetup.h @@ -143,6 +143,13 @@ namespace JCAT { */ VkQueue presentQueue(); + /** + * @brief Retrieves the Vulkan instance being used. + * + * @return VkInstance The Vulkan instance for passing Vulkan information to implementation + */ + VkInstance getInstance(); + SwapChainSupportDetails getSwapChainSupport(); VkFormat findSupportedDepthFormat(const std::vector& candidates, VkImageTiling tiling, VkFormatFeatureFlags features); uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties); diff --git a/source/engine/src/deviceSetup.cpp b/source/engine/src/deviceSetup.cpp index 07fc452..b25031a 100644 --- a/source/engine/src/deviceSetup.cpp +++ b/source/engine/src/deviceSetup.cpp @@ -49,6 +49,10 @@ namespace JCAT { return presentQueue_; } + VkInstance DeviceSetup::getInstance() { + return instance; + } + SwapChainSupportDetails DeviceSetup::getSwapChainSupport() { return querySwapChainSupport(physicalDevice); } diff --git a/source/engine/src/graphicsPipeline.cpp b/source/engine/src/graphicsPipeline.cpp index 3f498c2..5927c4f 100644 --- a/source/engine/src/graphicsPipeline.cpp +++ b/source/engine/src/graphicsPipeline.cpp @@ -560,7 +560,9 @@ namespace JCAT { pipelineInfo.basePipelineIndex = -1; pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; - + + // On Nvidia GPUs, the below function sometimes causes the program to crash + // without any printed error messages (including the std::runtime_error) if (vkCreateGraphicsPipelines(device.device(), VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) { throw std::runtime_error("Failed to create this graphics pipeline!"); } diff --git a/source/engine/src/swapChain.cpp b/source/engine/src/swapChain.cpp index b4a2a95..35ab331 100644 --- a/source/engine/src/swapChain.cpp +++ b/source/engine/src/swapChain.cpp @@ -171,6 +171,7 @@ namespace JCAT { // Submit the draw command buffer if (vkQueueSubmit(device.graphicsQueue(), 1, &submitInfo, inFlightFences[currentFrame]) != VK_SUCCESS) { + // On Nvidia GPUs, VK_ERROR_DEVICE_LOST is sometimes returned by above function, causing error to be thrown throw std::runtime_error("Failed to submit the draw command buffer!"); }