diff --git a/.gitmodules b/.gitmodules index 698dc0a..6f23a6c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "extern/Modules"] path = extern/Modules url = https://github.com/elushaX/Modules.git +[submodule "extern/imgui"] + path = extern/imgui + url = https://github.com/ocornut/imgui.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 4794cba..e453ee9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,8 +4,7 @@ project(App) set(CMAKE_CXX_STANDARD 26) add_subdirectory(extern/Modules) +add_subdirectory(extern/Utils) -add_subdirectory(src/Common) -add_subdirectory(src/GPU) -add_subdirectory(src/PlatformWindow) +add_subdirectory(src/VulkanGraphics) add_subdirectory(src/App) diff --git a/src/Common/CMakeLists.txt b/extern/Utils/CMakeLists.txt similarity index 93% rename from src/Common/CMakeLists.txt rename to extern/Utils/CMakeLists.txt index 70e2ae4..82767c9 100644 --- a/src/Common/CMakeLists.txt +++ b/extern/Utils/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.30) -project(Common) +project(Utils) file(GLOB SOURCES "./private/*.cpp") add_library(${PROJECT_NAME} ${SOURCES}) diff --git a/src/Common/private/utils.cpp b/extern/Utils/private/utils.cpp similarity index 100% rename from src/Common/private/utils.cpp rename to extern/Utils/private/utils.cpp diff --git a/src/Common/private/stb_image.h b/extern/Utils/public/stb_image.h similarity index 100% rename from src/Common/private/stb_image.h rename to extern/Utils/public/stb_image.h diff --git a/src/Common/private/stb_image_write.h b/extern/Utils/public/stb_image_write.h similarity index 100% rename from src/Common/private/stb_image_write.h rename to extern/Utils/public/stb_image_write.h diff --git a/src/Common/public/utils.hpp b/extern/Utils/public/utils.hpp similarity index 100% rename from src/Common/public/utils.hpp rename to extern/Utils/public/utils.hpp diff --git a/extern/imgui b/extern/imgui new file mode 160000 index 0000000..5b7feeb --- /dev/null +++ b/extern/imgui @@ -0,0 +1 @@ +Subproject commit 5b7feebfd8f3e58392d955b4c8ae01f77eae25ed diff --git a/src/App/CMakeLists.txt b/src/App/CMakeLists.txt index 92412c1..1c0d2e8 100644 --- a/src/App/CMakeLists.txt +++ b/src/App/CMakeLists.txt @@ -5,6 +5,6 @@ project(App) file(GLOB SOURCES "./private/*.cpp") add_executable(${PROJECT_NAME} ${SOURCES}) target_include_directories(${PROJECT_NAME} PUBLIC public) -target_link_libraries(${PROJECT_NAME} PUBLIC Common GPU PlatformWindow) +target_link_libraries(${PROJECT_NAME} PUBLIC VulkanGraphics) file(COPY shaders DESTINATION ${PROJECT_BINARY_DIR}/) \ No newline at end of file diff --git a/src/App/private/main.cpp b/src/App/private/main.cpp index b0899e4..76dc65f 100644 --- a/src/App/private/main.cpp +++ b/src/App/private/main.cpp @@ -1,12 +1,29 @@ -#include "app_interactive.hpp" -#include "app_offscreen.hpp" +#include "VulkanWindow.hpp" +#include "VulkanOffscreen.hpp" + +#include "renderer.hpp" #include int runOffscreen() { try { - AppOffscreen app; - app.run(); + VulkanOffscreen app; + Renderer renderer; + + renderer.create(app.getRenderInfo()); + app.setRenderPass(renderer.getRenderPass()); + + auto frameData = app.beginDrawFrame(); + + renderer.fillRenderCommands(frameData.commandBuffer, frameData.framebuffer, frameData.extent2D); + renderer.updateUniformBuffer({frameData.extent2D.width, frameData.extent2D.height}); + + app.endDrawFrame(frameData); + + app.saveFrame(); + + renderer.destroy(app.getDevice()); + } catch (const std::exception &e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; @@ -16,10 +33,28 @@ int runOffscreen() { } int runInteractive() { - AppInteractive app; try { - app.run(); + Renderer renderer; + VulkanWindow app; + + renderer.create(app.getRenderInfo()); + app.setRenderPass(renderer.getRenderPass()); + + while (!glfwWindowShouldClose(app.getWindow())) { + app.pollEvents(); + + auto drawData = app.startDrawFrame(); + + renderer.fillRenderCommands(drawData.commandBuffer, drawData.frameBuffer, drawData.extent2D); + renderer.updateUniformBuffer({drawData.extent2D.width, drawData.extent2D.height}); + + app.endDrawFrame(drawData); + } + + app.waitDevice(); + renderer.destroy(app.getDevice()); + } catch (const std::exception &e) { std::cerr << e.what() << std::endl; return EXIT_FAILURE; diff --git a/src/App/private/renderer.cpp b/src/App/private/renderer.cpp index 036df18..5be28b3 100644 --- a/src/App/private/renderer.cpp +++ b/src/App/private/renderer.cpp @@ -1,10 +1,10 @@ #include "renderer.hpp" -#include "vulkan_utils.hpp" +#include "VulkanUtils.hpp" #include #include -void Renderer::create(const CreateInfo &info) { +void Renderer::create(const RenderCreateInfo &info) { mShader.create(info.device); createRenderPass(info.device, info.format, info.finalImageLayout); @@ -371,7 +371,7 @@ void Renderer::destroyBuffer(VkDevice device, VkBuffer buffer, VkDeviceMemory me } void -Renderer::populateGraphicsCommandBuffer(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer, VkExtent2D extend) { +Renderer::fillRenderCommands(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer, VkExtent2D extend) { VkCommandBufferBeginInfo commandBufferBeginInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, }; @@ -471,4 +471,25 @@ void Renderer::copyBuffer(VkDevice device, VkCommandPool commandPool, VkQueue qu vkQueueWaitIdle(queue); vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer); +} + +void Renderer::updateUniformBuffer(const std::pair& frameBufferSize) const { + static float time = 0; + time += 0.001; + + glm::mat4 model = glm::rotate(glm::mat4(1.0f), (time * glm::radians(90.f)), glm::vec3(0.f, 0.f, 1.f)); + glm::mat4 view = glm::lookAt(glm::vec3{2.0f, 2.0f, 2.0f}, glm::vec3{0.0f, 0.0f, 0.0f}, -glm::vec3{0.0f, 0.0f, 1.0f}); + glm::mat4 perspective = glm::perspective(glm::radians(45.f), + (float) frameBufferSize.first / + (float) frameBufferSize.second, 0.1f, 10.f); + + auto transforms = perspective * view * model; + + CustomShader::UniformBuffer ubo{ + .transforms = transforms, + .origin = glm::vec4(0, 0, 0, 0), + }; + + memcpy(mUniformBufferMemoryMapped, &ubo, sizeof(ubo)); + // ubo.transforms = } \ No newline at end of file diff --git a/src/Common/public/common.hpp b/src/App/public/common.hpp similarity index 100% rename from src/Common/public/common.hpp rename to src/App/public/common.hpp diff --git a/src/App/public/renderer.hpp b/src/App/public/renderer.hpp index 00ab2cc..03b8ea9 100644 --- a/src/App/public/renderer.hpp +++ b/src/App/public/renderer.hpp @@ -3,39 +3,11 @@ #include "shader.hpp" #include -struct Renderer { - CustomShader mShader; - VkPipeline mGraphicsPipeline = VK_NULL_HANDLE; - VkRenderPass mGraphicsRenderPass = VK_NULL_HANDLE; - VkPipelineLayout mGraphicsPipelineLayout{}; // no uniforms used in the shader +class Renderer { +public: + Renderer() = default; - // Buffers - VkBuffer mVertexBuffer = VK_NULL_HANDLE; - VkDeviceMemory mVertexBufferMemory = VK_NULL_HANDLE; - - VkBuffer mIndexBuffer = VK_NULL_HANDLE; - VkDeviceMemory mIndexBufferMemory = VK_NULL_HANDLE; - - VkDescriptorPool mDescriptorPool = VK_NULL_HANDLE; - VkDescriptorSet mDescriptorSet = VK_NULL_HANDLE; - - VkBuffer mUniformBuffer = VK_NULL_HANDLE; - VkDeviceMemory mUniformBufferMemory = VK_NULL_HANDLE; - void* mUniformBufferMemoryMapped = nullptr; - - struct CreateInfo { - VkDevice device; - VkPhysicalDevice physicalDevice; - VkExtent2D extent2D; - VkFormat format; - - VkCommandPool commandPool; - VkQueue queue; - - VkImageLayout finalImageLayout; - }; - - void create(const CreateInfo& info); + void create(const RenderCreateInfo& info); void destroy(VkDevice device) const; @@ -56,7 +28,7 @@ struct Renderer { void createUniformBuffer(VkPhysicalDevice physicalDevice, VkDevice device); - void populateGraphicsCommandBuffer(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer, VkExtent2D extend); + void fillRenderCommands(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer, VkExtent2D extend); struct BufferCreateInfo { VkBuffer *buffer; @@ -74,4 +46,28 @@ struct Renderer { static void destroyBuffer(VkDevice device, VkBuffer buffer, VkDeviceMemory memory); void destroyGraphicsPipeline(VkDevice device) const; + + void updateUniformBuffer(const std::pair& frameBufferSize) const; + + VkRenderPass getRenderPass() { return mGraphicsRenderPass; } + +private: + CustomShader mShader; + VkPipeline mGraphicsPipeline = VK_NULL_HANDLE; + VkRenderPass mGraphicsRenderPass = VK_NULL_HANDLE; + VkPipelineLayout mGraphicsPipelineLayout{}; // no uniforms used in the shader + + // Buffers + VkBuffer mVertexBuffer = VK_NULL_HANDLE; + VkDeviceMemory mVertexBufferMemory = VK_NULL_HANDLE; + + VkBuffer mIndexBuffer = VK_NULL_HANDLE; + VkDeviceMemory mIndexBufferMemory = VK_NULL_HANDLE; + + VkDescriptorPool mDescriptorPool = VK_NULL_HANDLE; + VkDescriptorSet mDescriptorSet = VK_NULL_HANDLE; + + VkBuffer mUniformBuffer = VK_NULL_HANDLE; + VkDeviceMemory mUniformBufferMemory = VK_NULL_HANDLE; + void* mUniformBufferMemoryMapped = nullptr; }; \ No newline at end of file diff --git a/src/App/public/shader.hpp b/src/App/public/shader.hpp index 4e5fc48..8d56058 100644 --- a/src/App/public/shader.hpp +++ b/src/App/public/shader.hpp @@ -1,8 +1,8 @@ #pragma once -#include "common.hpp" +#include "VulkanInterface.hpp" -#include +#include "common.hpp" struct CustomShader { diff --git a/src/GPU/private/GPU.cpp b/src/GPU/private/GPU.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/src/GPU/public/GPU.hpp b/src/GPU/public/GPU.hpp deleted file mode 100644 index 7b9637e..0000000 --- a/src/GPU/public/GPU.hpp +++ /dev/null @@ -1 +0,0 @@ -#pragma once \ No newline at end of file diff --git a/src/PlatformWindow/CMakeLists.txt b/src/PlatformWindow/CMakeLists.txt deleted file mode 100644 index bb1513d..0000000 --- a/src/PlatformWindow/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -cmake_minimum_required(VERSION 3.30) - -project(PlatformWindow) - -file(GLOB SOURCES "./private/*.cpp") -add_library(${PROJECT_NAME} ${SOURCES}) -target_include_directories(${PROJECT_NAME} PUBLIC public) -target_link_libraries(${PROJECT_NAME} PUBLIC glfw GPU) \ No newline at end of file diff --git a/src/PlatformWindow/private/PlatformWindow.cpp b/src/PlatformWindow/private/PlatformWindow.cpp deleted file mode 100644 index e69de29..0000000 diff --git a/src/PlatformWindow/public/PlatformWindow.hpp b/src/PlatformWindow/public/PlatformWindow.hpp deleted file mode 100644 index 7b9637e..0000000 --- a/src/PlatformWindow/public/PlatformWindow.hpp +++ /dev/null @@ -1 +0,0 @@ -#pragma once \ No newline at end of file diff --git a/src/GPU/CMakeLists.txt b/src/VulkanGraphics/CMakeLists.txt similarity index 62% rename from src/GPU/CMakeLists.txt rename to src/VulkanGraphics/CMakeLists.txt index 40d6b6e..26ec784 100644 --- a/src/GPU/CMakeLists.txt +++ b/src/VulkanGraphics/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required(VERSION 3.30) -project(GPU) +project(VulkanGraphics) file(GLOB SOURCES "./private/*.cpp") add_library(${PROJECT_NAME} ${SOURCES}) target_include_directories(${PROJECT_NAME} PUBLIC public) -target_link_libraries(${PROJECT_NAME} PUBLIC vulkan Common) \ No newline at end of file +target_link_libraries(${PROJECT_NAME} PUBLIC vulkan glfw Containers Math Utils) \ No newline at end of file diff --git a/src/GPU/private/swapchain.cpp b/src/VulkanGraphics/private/SwapChain.cpp similarity index 99% rename from src/GPU/private/swapchain.cpp rename to src/VulkanGraphics/private/SwapChain.cpp index a6083cb..f98d915 100644 --- a/src/GPU/private/swapchain.cpp +++ b/src/VulkanGraphics/private/SwapChain.cpp @@ -1,4 +1,5 @@ -#include "swapchain.hpp" +#include "SwapChain.hpp" + #include #include #include diff --git a/src/GPU/public/swapchain.hpp b/src/VulkanGraphics/private/SwapChain.hpp similarity index 100% rename from src/GPU/public/swapchain.hpp rename to src/VulkanGraphics/private/SwapChain.hpp diff --git a/src/App/private/app_offscreen.cpp b/src/VulkanGraphics/private/VulkanOffscreen.cpp similarity index 83% rename from src/App/private/app_offscreen.cpp rename to src/VulkanGraphics/private/VulkanOffscreen.cpp index 08ebf86..9baca17 100644 --- a/src/App/private/app_offscreen.cpp +++ b/src/VulkanGraphics/private/VulkanOffscreen.cpp @@ -1,23 +1,15 @@ -#include "app_offscreen.hpp" +#include "VulkanOffscreen.hpp" +#include "Buffer2D.hpp" +#include "Color.hpp" #include "utils.hpp" -AppOffscreen::AppOffscreen() { +VulkanOffscreen::VulkanOffscreen() { initDevice(); initPipeline(); - initFramebuffer(); - initStagingBuffer(); } -void AppOffscreen::run() { - updateUniformBuffer(); - draw(); - transitionImageLayout(); - transferToHost(); - readMemory(); -} - -AppOffscreen::~AppOffscreen() { +VulkanOffscreen::~VulkanOffscreen() { vkDeviceWaitIdle(mDevice); finalizeStagingBuffer(); @@ -26,7 +18,55 @@ AppOffscreen::~AppOffscreen() { finalizeDevice(); } -void AppOffscreen::initDevice() { +RenderCreateInfo VulkanOffscreen::getRenderInfo() { + return { + .device = mDevice, + .physicalDevice = mPhysicalDevice, + .extent2D = mExtent, + .format = mFormat, + .commandPool = mCommandPool, + .queue = mGraphicsQueue, + .finalImageLayout = VK_IMAGE_LAYOUT_GENERAL, + // .finalImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + }; +} + +void VulkanOffscreen::setRenderPass(VkRenderPass renderPass) { + initFramebuffer(renderPass); + initStagingBuffer(); +} + +VulkanOffscreen::FrameData VulkanOffscreen::beginDrawFrame() { + return { + mCommandBuffer, mFramebuffer, mExtent, + }; +} + +void VulkanOffscreen::endDrawFrame(const FrameData&) { + VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + + VkSubmitInfo submitInfo{ + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .waitSemaphoreCount = 0, + .pWaitSemaphores = nullptr, + .pWaitDstStageMask = waitStages, + .commandBufferCount = 1, + .pCommandBuffers = &mCommandBuffer, + .signalSemaphoreCount = 0, + .pSignalSemaphores = nullptr, + }; + + if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, nullptr) != VK_SUCCESS) { + throw std::runtime_error("failed to submit to graphics queue"); + } +} + +void VulkanOffscreen::saveFrame() { + transferToHost(); + readMemory(); +} + +void VulkanOffscreen::initDevice() { std::vector extensions = {}; std::vector validationLayers = {}; std::vector deviceExtensions = {}; @@ -97,8 +137,7 @@ void AppOffscreen::initDevice() { vkGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue); } -void AppOffscreen::initPipeline() { - mShader.create(mDevice); +void VulkanOffscreen::initPipeline() { VkCommandPoolCreateInfo commandPoolCreateInfo { .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, @@ -117,22 +156,9 @@ void AppOffscreen::initPipeline() { }; vkAllocateCommandBuffers(mDevice, &allocateInfo, &mCommandBuffer); - - Renderer::CreateInfo renderCreateInfo { - .device = mDevice, - .physicalDevice = mPhysicalDevice, - .extent2D = mExtent, - .format = mFormat, - .commandPool = mCommandPool, - .queue = mGraphicsQueue, - .finalImageLayout = VK_IMAGE_LAYOUT_GENERAL, - // .finalImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - }; - - mRenderer.create(renderCreateInfo); } -void AppOffscreen::initFramebuffer() { +void VulkanOffscreen::initFramebuffer(VkRenderPass renderPass) { // vkGetDeviceImageMemoryRequirements(); VkImageCreateInfo imageCreateInfo { @@ -193,7 +219,7 @@ void AppOffscreen::initFramebuffer() { VkFramebufferCreateInfo createInfo { .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - .renderPass = mRenderer.mGraphicsRenderPass, + .renderPass = renderPass, .attachmentCount = 1, .pAttachments = &mImageView, .width = mExtent.width, @@ -206,32 +232,7 @@ void AppOffscreen::initFramebuffer() { } } -void AppOffscreen::draw() { - mRenderer.populateGraphicsCommandBuffer(mCommandBuffer, mFramebuffer, mExtent); - - VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; - - VkSubmitInfo submitInfo{ - .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, - .waitSemaphoreCount = 0, - .pWaitSemaphores = nullptr, - .pWaitDstStageMask = waitStages, - .commandBufferCount = 1, - .pCommandBuffers = &mCommandBuffer, - .signalSemaphoreCount = 0, - .pSignalSemaphores = nullptr, - }; - - if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, nullptr) != VK_SUCCESS) { - throw std::runtime_error("failed to submit to graphics queue"); - } -} - -void AppOffscreen::transitionImageLayout() { - -} - -void AppOffscreen::transferToHost() { +void VulkanOffscreen::transferToHost() { vkDeviceWaitIdle(mDevice); VkBufferImageCopy stagingBufferToImageCopy { @@ -289,7 +290,7 @@ void AppOffscreen::transferToHost() { } } -void AppOffscreen::readMemory() { +void VulkanOffscreen::readMemory() { void* data; uint32_t size = mExtent.height * mExtent.width * sizeof(float) * 4; if (vkMapMemory(mDevice, mStagingBufferMemory, 0, size, 0, &data) != VK_SUCCESS) { @@ -304,25 +305,7 @@ void AppOffscreen::readMemory() { vkUnmapMemory(mDevice, mStagingBufferMemory); } -void AppOffscreen::updateUniformBuffer() const { - static float time = 0; - time += 0.001; - - glm::mat4 model = glm::rotate(glm::mat4(1.0f), (time * glm::radians(90.f)), glm::vec3(0.f, 0.f, 1.f)); - glm::mat4 view = glm::lookAt(glm::vec3{2.0f, 2.0f, 2.0f}, glm::vec3{0.0f, 0.0f, 0.0f}, -glm::vec3{0.0f, 0.0f, 1.0f}); - glm::mat4 perspective = glm::perspective(glm::radians(45.f), (float) mExtent.width / (float) mExtent.height, 0.1f, 10.f); - - auto transforms = perspective * view * model; - - CustomShader::UniformBuffer ubo{ - .transforms = transforms, - .origin = glm::vec4(0, 0, 0, 0), - }; - - memcpy(mRenderer.mUniformBufferMemoryMapped, &ubo, sizeof(ubo)); -} - -void AppOffscreen::initStagingBuffer() { +void VulkanOffscreen::initStagingBuffer() { VkBufferCreateInfo stagingBufferCreateInfo { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, @@ -346,27 +329,24 @@ void AppOffscreen::initStagingBuffer() { vkBindBufferMemory(mDevice, mStagingBuffer, mStagingBufferMemory, 0); } -void AppOffscreen::finalizeStagingBuffer() { +void VulkanOffscreen::finalizeStagingBuffer() { vkFreeMemory(mDevice, mStagingBufferMemory, nullptr); vkDestroyBuffer(mDevice, mStagingBuffer, nullptr); } -void AppOffscreen::finalizeFramebuffer() { +void VulkanOffscreen::finalizeFramebuffer() { vkFreeMemory(mDevice, mFramebufferMemory, nullptr); vkDestroyFramebuffer(mDevice, mFramebuffer, nullptr); vkDestroyImageView(mDevice, mImageView, nullptr); vkDestroyImage(mDevice, mImage, nullptr); } -void AppOffscreen::finalizePipeline() { +void VulkanOffscreen::finalizePipeline() { vkFreeCommandBuffers(mDevice, mCommandPool, 1, &mCommandBuffer); vkDestroyCommandPool(mDevice, mCommandPool, nullptr); - - mShader.destroy(mDevice); - mRenderer.destroy(mDevice); } -void AppOffscreen::finalizeDevice() { +void VulkanOffscreen::finalizeDevice() { vkDestroyDevice(mDevice, nullptr); vkDestroyInstance(mInstance, nullptr); } diff --git a/src/GPU/private/vulkan_utils.cpp b/src/VulkanGraphics/private/VulkanUtils.cpp similarity index 99% rename from src/GPU/private/vulkan_utils.cpp rename to src/VulkanGraphics/private/VulkanUtils.cpp index f5ba879..375500a 100644 --- a/src/GPU/private/vulkan_utils.cpp +++ b/src/VulkanGraphics/private/VulkanUtils.cpp @@ -1,4 +1,4 @@ -#include "vulkan_utils.hpp" +#include "VulkanUtils.hpp" #include #include diff --git a/src/App/private/app_interactive.cpp b/src/VulkanGraphics/private/VulkanWindow.cpp similarity index 72% rename from src/App/private/app_interactive.cpp rename to src/VulkanGraphics/private/VulkanWindow.cpp index c939e85..2d3d2dd 100644 --- a/src/App/private/app_interactive.cpp +++ b/src/VulkanGraphics/private/VulkanWindow.cpp @@ -1,32 +1,69 @@ -#include "app_interactive.hpp" - -#include - -#include "renderer.hpp" -#include "swapchain.hpp" -#include "vulkan_utils.hpp" +#include "VulkanWindow.hpp" +#include "SwapChain.hpp" #include #include #include #include +#include + +VulkanWindow::VulkanWindow() { + mSwapChain = new SwapChain(); -void AppInteractive::run() { initWindow(); initVulkan(); - mainLoop(); - cleanup(); } -void AppInteractive::scheduleWindowResize(int sizeX, int sizeY) { +VulkanWindow::~VulkanWindow() { + cleanup(); + delete mSwapChain; +} + +RenderCreateInfo VulkanWindow::getRenderInfo() { + return { + mDevice, mPhysicalDevice, + mSwapChain->mSwapChainExtent, mSwapChain->mSwapChainFormat, + mCommandPool, mGraphicsQueue, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + }; +} + +GLFWwindow* VulkanWindow::getWindow() { + return mWindow; +} + +VkDevice VulkanWindow::getDevice() { + return mDevice; +} + +void VulkanWindow::setRenderPass(VkRenderPass renderPass) { + mRenderPass = renderPass; + mSwapChain->createSwapChainFramebuffers(mDevice, mRenderPass); +} + +void VulkanWindow::pollEvents() { + glfwPollEvents(); + + if (mWindowSizeDirtyFlag) { + if (timeDeltaMs(mWindowSizeDirtyFlagTime) > mWindowSizeApplyMinDelay) { + recreateSwapChain(mWindowFramebufferSize.first, mWindowFramebufferSize.second); + mWindowSizeDirtyFlag = false; + } + } +} + +void VulkanWindow::waitDevice() { + vkDeviceWaitIdle(mDevice); +} + +void VulkanWindow::scheduleWindowResize(int sizeX, int sizeY) { mWindowSizeDirtyFlagTime = std::chrono::system_clock::now(); mWindowSizeDirtyFlag = true; mWindowFramebufferSize = std::make_pair(sizeX, sizeY); assert(!(sizeX <= 0 || sizeY <= 0)); } -void AppInteractive::initWindow() { +void VulkanWindow::initWindow() { // glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11); glfwInit(); @@ -37,11 +74,11 @@ void AppInteractive::initWindow() { glfwSetWindowUserPointer(mWindow, this); glfwSetFramebufferSizeCallback(mWindow, [](GLFWwindow *window, int sizeX, int sizeY) { - ((AppInteractive *) glfwGetWindowUserPointer(window))->scheduleWindowResize(sizeX, sizeY); + ((VulkanWindow *) glfwGetWindowUserPointer(window))->scheduleWindowResize(sizeX, sizeY); }); } -void AppInteractive::initVulkan() { +void VulkanWindow::initVulkan() { const std::vector deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, }; @@ -105,44 +142,16 @@ void AppInteractive::initVulkan() { .presentationQueueFamilyIndex = (uint32_t) mPresentationQueueFamilyIndex, }; - mSwapChain.createSwapChain(swapChainCreateInfo); - mSwapChain.createSwapChainImageViews(mDevice); + mSwapChain->createSwapChain(swapChainCreateInfo); + mSwapChain->createSwapChainImageViews(mDevice); createCommandPool(); createCommandBuffer(); - Renderer::CreateInfo rendererCreateInfo{ - mDevice, mPhysicalDevice, - mSwapChain.mSwapChainExtent, mSwapChain.mSwapChainFormat, - mCommandPool, mGraphicsQueue, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR - }; - - mRenderer.create(rendererCreateInfo); - - mSwapChain.createSwapChainFramebuffers(mDevice, mRenderer.mGraphicsRenderPass); - createSynchronizationObjects(); } -void AppInteractive::mainLoop() { - while (!glfwWindowShouldClose(mWindow)) { - glfwPollEvents(); - - if (mWindowSizeDirtyFlag) { - auto timeDelayMs = timeDeltaMs(mWindowSizeDirtyFlagTime); - if (timeDelayMs > mWindowSizeApplyMinDelay) { - recreateSwapChain(mWindowFramebufferSize.first, mWindowFramebufferSize.second); - mWindowSizeDirtyFlag = false; - } - } - - drawFrame(); - } - - vkDeviceWaitIdle(mDevice); -} - -void AppInteractive::createLogicalDevice(const std::vector &deviceExtensions) { +void VulkanWindow::createLogicalDevice(const std::vector &deviceExtensions) { float queuePriority = 1.f; VkDeviceQueueCreateInfo graphicsQueueCreateInfos{ @@ -181,21 +190,21 @@ void AppInteractive::createLogicalDevice(const std::vector &device if (mDevice == VK_NULL_HANDLE) throw std::runtime_error("failed to create vulkan logical device"); } -void AppInteractive::getQueues() { +void VulkanWindow::getQueues() { vkGetDeviceQueue(mDevice, mGraphicsQueueFamilyIndex, 0, &mGraphicsQueue); vkGetDeviceQueue(mDevice, mPresentationQueueFamilyIndex, 0, &mPresentQueue); } -void AppInteractive::createWindowSurface() { +void VulkanWindow::createWindowSurface() { if (glfwCreateWindowSurface(mInstance, mWindow, nullptr, &mSurface) != VK_SUCCESS) { throw std::runtime_error("failed to create vulkan window surface"); } } -void AppInteractive::recreateSwapChain(int sizeX, int sizeY) { +void VulkanWindow::recreateSwapChain(int sizeX, int sizeY) { vkDeviceWaitIdle(mDevice); - mSwapChain.destroySwapChain(mDevice); + mSwapChain->destroySwapChain(mDevice); SwapChain::CreateInfo createInfo{ .device = mDevice, @@ -207,13 +216,12 @@ void AppInteractive::recreateSwapChain(int sizeX, int sizeY) { .presentationQueueFamilyIndex = (uint32_t) mPresentationQueueFamilyIndex, }; - mSwapChain.createSwapChain(createInfo); - mSwapChain.createSwapChainImageViews(mDevice); - mSwapChain.createSwapChainFramebuffers(mDevice, mRenderer.mGraphicsRenderPass); + mSwapChain->createSwapChain(createInfo); + mSwapChain->createSwapChainImageViews(mDevice); + mSwapChain->createSwapChainFramebuffers(mDevice, mRenderPass); } - -void AppInteractive::createSynchronizationObjects() { +void VulkanWindow::createSynchronizationObjects() { VkSemaphoreCreateInfo semaphoreCreateInfo{ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, }; @@ -232,18 +240,27 @@ void AppInteractive::createSynchronizationObjects() { if (failure) throw std::runtime_error("failed to create synchronization objects"); } -void AppInteractive::drawFrame() { +VulkanWindow::DrawFrame VulkanWindow::startDrawFrame() { vkWaitForFences(mDevice, 1, &mFenceCanStartNewFrame, VK_TRUE, UINT64_MAX); vkResetFences(mDevice, 1, &mFenceCanStartNewFrame); uint32_t imageIndex = 0; - vkAcquireNextImageKHR(mDevice, mSwapChain.mSwapChain, UINT64_MAX, mSemaphoreImageAcquired, VK_NULL_HANDLE, + vkAcquireNextImageKHR(mDevice, mSwapChain->mSwapChain, UINT64_MAX, mSemaphoreImageAcquired, VK_NULL_HANDLE, &imageIndex); vkResetCommandBuffer(mCommandBuffer, 0); - mRenderer.populateGraphicsCommandBuffer(mCommandBuffer, mSwapChain.mSwapChainFrameBuffers[imageIndex], - mSwapChain.mSwapChainExtent); + VkFramebuffer framebuffer = mSwapChain->mSwapChainFrameBuffers[imageIndex]; + + return { + .commandBuffer = mCommandBuffer, + .frameBuffer = framebuffer, + .extent2D = mSwapChain->mSwapChainExtent, + .swapChainImageIndex = imageIndex, + }; +} + +void VulkanWindow::endDrawFrame(const DrawFrame& frame) { VkSemaphore waitSemaphores[] = {mSemaphoreImageAcquired}; VkSemaphore signalSemaphores[] = {mSemaphoreFramebufferDrawn}; VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; @@ -259,13 +276,11 @@ void AppInteractive::drawFrame() { .pSignalSemaphores = signalSemaphores, }; - updateUniformBuffer(); - if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, mFenceCanStartNewFrame) != VK_SUCCESS) { throw std::runtime_error("failed to submit to graphics queue"); } - VkSwapchainKHR swapchains[] = {mSwapChain.mSwapChain}; + VkSwapchainKHR swapchains[] = {mSwapChain->mSwapChain}; VkPresentInfoKHR presentInfo{ .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, @@ -273,53 +288,23 @@ void AppInteractive::drawFrame() { .pWaitSemaphores = signalSemaphores, .swapchainCount = 1, .pSwapchains = swapchains, - .pImageIndices = &imageIndex, + .pImageIndices = &frame.swapChainImageIndex, }; vkQueuePresentKHR(mPresentQueue, &presentInfo); - /* Somehow res is always VK_SUCCESS - if (res == VK_ERROR_OUT_OF_DATE_KHR || res == VK_SUBOPTIMAL_KHR) { - recreateSwapChain(); - } else if (res != VK_SUCCESS) { - throw std::runtime_error("cannot acquire new khr image"); - } - */ } -void AppInteractive::updateUniformBuffer() const { - static float time = 0; - time += 0.001; - - glm::mat4 model = glm::rotate(glm::mat4(1.0f), (time * glm::radians(90.f)), glm::vec3(0.f, 0.f, 1.f)); - glm::mat4 view = glm::lookAt(glm::vec3{2.0f, 2.0f, 2.0f}, glm::vec3{0.0f, 0.0f, 0.0f}, -glm::vec3{0.0f, 0.0f, 1.0f}); - glm::mat4 perspective = glm::perspective(glm::radians(45.f), - (float) mWindowFramebufferSize.first / - (float) mWindowFramebufferSize.second, 0.1f, 10.f); - - auto transforms = perspective * view * model; - - CustomShader::UniformBuffer ubo{ - .transforms = transforms, - .origin = glm::vec4(0, 0, 0, 0), - }; - - memcpy(mRenderer.mUniformBufferMemoryMapped, &ubo, sizeof(ubo)); - // ubo.transforms = -} - -void AppInteractive::destroySynchronizationObjects() { +void VulkanWindow::destroySynchronizationObjects() { vkDestroySemaphore(mDevice, mSemaphoreImageAcquired, nullptr); vkDestroySemaphore(mDevice, mSemaphoreFramebufferDrawn, nullptr); vkDestroyFence(mDevice, mFenceCanStartNewFrame, nullptr); } -void AppInteractive::cleanup() { - mRenderer.destroy(mDevice); - +void VulkanWindow::cleanup() { destroyCommandPool(); destroySynchronizationObjects(); - mSwapChain.destroySwapChain(mDevice); + mSwapChain->destroySwapChain(mDevice); vkDestroyDevice(mDevice, nullptr); vkDestroySurfaceKHR(mInstance, mSurface, nullptr); @@ -328,7 +313,7 @@ void AppInteractive::cleanup() { glfwTerminate(); } -void AppInteractive::createCommandPool() { +void VulkanWindow::createCommandPool() { VkCommandPoolCreateInfo createInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, @@ -340,7 +325,7 @@ void AppInteractive::createCommandPool() { } } -void AppInteractive::createCommandBuffer() { +void VulkanWindow::createCommandBuffer() { VkCommandBufferAllocateInfo allocateInfo{ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .commandPool = mCommandPool, @@ -353,6 +338,6 @@ void AppInteractive::createCommandBuffer() { } } -void AppInteractive::destroyCommandPool() { +void VulkanWindow::destroyCommandPool() { vkDestroyCommandPool(mDevice, mCommandPool, nullptr); } diff --git a/src/VulkanGraphics/public/VulkanInterface.hpp b/src/VulkanGraphics/public/VulkanInterface.hpp new file mode 100644 index 0000000..e2bcd99 --- /dev/null +++ b/src/VulkanGraphics/public/VulkanInterface.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include + +struct RenderCreateInfo { + VkDevice device; + VkPhysicalDevice physicalDevice; + VkExtent2D extent2D; + VkFormat format; + + VkCommandPool commandPool; + VkQueue queue; + + VkImageLayout finalImageLayout; +}; \ No newline at end of file diff --git a/src/App/public/app_offscreen.hpp b/src/VulkanGraphics/public/VulkanOffscreen.hpp similarity index 68% rename from src/App/public/app_offscreen.hpp rename to src/VulkanGraphics/public/VulkanOffscreen.hpp index e1a677f..a8258bd 100644 --- a/src/App/public/app_offscreen.hpp +++ b/src/VulkanGraphics/public/VulkanOffscreen.hpp @@ -1,39 +1,44 @@ #pragma once -#include "vulkan_utils.hpp" +#include "VulkanUtils.hpp" + #include #include #include -#include "renderer.hpp" -#include "shader.hpp" - -class AppOffscreen { +class VulkanOffscreen { public: - AppOffscreen(); + VulkanOffscreen(); + ~VulkanOffscreen(); - void run(); + RenderCreateInfo getRenderInfo(); + void setRenderPass(VkRenderPass renderPass); - ~AppOffscreen(); + struct FrameData { + VkCommandBuffer commandBuffer; + VkFramebuffer framebuffer; + VkExtent2D extent2D; + }; + + FrameData beginDrawFrame(); + void endDrawFrame(const FrameData& frame); + + void saveFrame(); + + VkDevice getDevice() { return mDevice; } private: void initDevice(); void initPipeline(); - void initFramebuffer(); + void initFramebuffer(VkRenderPass renderPass); void initStagingBuffer(); - void draw(); - - void transitionImageLayout(); - void readMemory(); - void updateUniformBuffer() const; - void transferToHost(); void finalizeStagingBuffer(); @@ -70,7 +75,4 @@ private: VkBuffer mStagingBuffer = VK_NULL_HANDLE; VkDeviceMemory mStagingBufferMemory = VK_NULL_HANDLE; - - Renderer mRenderer; - CustomShader mShader; }; \ No newline at end of file diff --git a/src/GPU/public/vulkan_utils.hpp b/src/VulkanGraphics/public/VulkanUtils.hpp similarity index 91% rename from src/GPU/public/vulkan_utils.hpp rename to src/VulkanGraphics/public/VulkanUtils.hpp index 25dad42..d8160be 100644 --- a/src/GPU/public/vulkan_utils.hpp +++ b/src/VulkanGraphics/public/VulkanUtils.hpp @@ -1,6 +1,7 @@ #pragma once -#include +#include "VulkanInterface.hpp" + #include #include @@ -15,4 +16,4 @@ int findQueueFamilyIndex(VkPhysicalDevice physicalDevice, const QueuePickPolicy& uint32_t findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags flags); VkInstance createInstance(const char ** instanceExtensions, uint32_t extensionsCount); -VkPhysicalDevice pickPhysicalDevice(VkInstance instance, const PhysicalDevicePickPolicy& pickPolicy); +VkPhysicalDevice pickPhysicalDevice(VkInstance instance, const PhysicalDevicePickPolicy& pickPolicy); \ No newline at end of file diff --git a/src/App/public/app_interactive.hpp b/src/VulkanGraphics/public/VulkanWindow.hpp similarity index 69% rename from src/App/public/app_interactive.hpp rename to src/VulkanGraphics/public/VulkanWindow.hpp index fd621b8..e8e8895 100644 --- a/src/App/public/app_interactive.hpp +++ b/src/VulkanGraphics/public/VulkanWindow.hpp @@ -1,16 +1,35 @@ #pragma once -#include "vulkan_utils.hpp" +#include "VulkanUtils.hpp" -#include "renderer.hpp" -#include "swapchain.hpp" #include "utils.hpp" #include -class AppInteractive { +class SwapChain; + +class VulkanWindow { public: - void run(); + VulkanWindow(); + ~VulkanWindow(); + + GLFWwindow* getWindow(); + VkDevice getDevice(); + RenderCreateInfo getRenderInfo(); + void setRenderPass(VkRenderPass renderPass); + + struct DrawFrame{ + VkCommandBuffer commandBuffer; + VkFramebuffer frameBuffer; + VkExtent2D extent2D; + uint32_t swapChainImageIndex; + }; + + void pollEvents(); + void waitDevice(); + + DrawFrame startDrawFrame(); + void endDrawFrame(const DrawFrame& frame); void scheduleWindowResize(int sizeX, int sizeY); @@ -19,8 +38,6 @@ private: void initVulkan(); - void mainLoop(); - void createLogicalDevice(const std::vector &deviceExtensions); void getQueues(); @@ -31,10 +48,6 @@ private: void createSynchronizationObjects(); - void drawFrame(); - - void updateUniformBuffer() const; - void destroySynchronizationObjects(); void cleanup(); @@ -67,9 +80,10 @@ private: VkQueue mPresentQueue = VK_NULL_HANDLE; VkSurfaceKHR mSurface = VK_NULL_HANDLE; - SwapChain mSwapChain; + SwapChain* mSwapChain = nullptr; - Renderer mRenderer; + VkRenderPass mRenderPass = VK_NULL_HANDLE; + // Renderer* mRenderer = nullptr; VkSemaphore mSemaphoreImageAcquired = VK_NULL_HANDLE; VkSemaphore mSemaphoreFramebufferDrawn = VK_NULL_HANDLE;