From 665878236a26ae85e540c013c817c55a1e446a99 Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:10:07 +0300 Subject: [PATCH 01/24] fix spdlog --- vcpkg | 2 +- vcpkg.json | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/vcpkg b/vcpkg index 9b75e789..b1b19307 160000 --- a/vcpkg +++ b/vcpkg @@ -1 +1 @@ -Subproject commit 9b75e789ece3f942159b8500584e35aafe3979ff +Subproject commit b1b19307e2d2ec1eefbdb7ea069de7d4bcd31f01 diff --git a/vcpkg.json b/vcpkg.json index e932e3d7..49225b84 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -29,6 +29,10 @@ { "name": "fastgltf", "version": "0.7.1" + }, + { + "name": "fmt", + "version": "11.2.0" } ] } \ No newline at end of file From 13ceb3cf0ded8b957605fefd635f6ae4c6a79050 Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:26:27 +0300 Subject: [PATCH 02/24] fix: memory leak --- src/graphics/vulkan/vk_engine.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index 99b94292..c29b0fcc 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -608,6 +608,7 @@ void VulkanEngine::cleanup() { // Destroy frame descriptors manually _frame._frameDescriptors.destroy_pools(_device); } + globalDescriptorAllocator.destroy_pools(_device); destroy_swapchain(); From 7b227ef8f75b25791a77c8add81ccaa9703ce28e Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:29:18 +0300 Subject: [PATCH 03/24] fix: RAII for ImGui resources --- src/graphics/vulkan/vk_engine.cpp | 80 ++++++++++++------------- src/include/graphics/vulkan/vk_engine.h | 9 +++ 2 files changed, 49 insertions(+), 40 deletions(-) diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index c29b0fcc..b87ab47b 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -190,68 +190,69 @@ void VulkanEngine::init_default_data() { globalDescriptorAllocator); } -void VulkanEngine::init_imgui() { + void VulkanEngine::Imgui::initImguiPool(const VkDevice& device) { // 1: create descriptor pool for IMGUI // the size of the pool is very oversize, but it's copied from imgui demo // itself. const VkDescriptorPoolSize pool_sizes[] = { - {VK_DESCRIPTOR_TYPE_SAMPLER, 1000}, - {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000}, - {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000}, - {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000}, - {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000}, - {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000}, - {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000}, - {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000}, - {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000}, - {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000}, - {VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000}}; - - VkDescriptorPoolCreateInfo pool_info = {}; - pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; - pool_info.maxSets = 1000; - pool_info.poolSizeCount = (uint32_t)std::size(pool_sizes); - pool_info.pPoolSizes = pool_sizes; - - VkDescriptorPool imguiPool; - VK_CHECK(vkCreateDescriptorPool(_device, &pool_info, nullptr, &imguiPool)); + {VK_DESCRIPTOR_TYPE_SAMPLER, 1000}, + {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000}, + {VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000}, + {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000}, + {VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000}, + {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000}, + {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000}, + {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000}, + {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000}, + {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000}, + {VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000}}; + VkDescriptorPoolCreateInfo pool_info = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, + .maxSets = 1000, + .poolSizeCount = (uint32_t)std::size(pool_sizes), + .pPoolSizes = pool_sizes}; + VK_CHECK(vkCreateDescriptorPool(device, &pool_info, nullptr, &_imguiPool)); +} + VulkanEngine::Imgui::Imgui(const VulkanEngine& engine) : _imguiPool{}, _device{engine._device} { + initImguiPool(engine._device); // 2: initialize imgui library - // this initializes the core structures of imgui ImGui::CreateContext(); - // this initializes imgui for SDL - ImGui_ImplSDL2_InitForVulkan(_window); - + ImGui_ImplSDL2_InitForVulkan(engine._window); // this initializes imgui for Vulkan - ImGui_ImplVulkan_InitInfo init_info = {}; - init_info.Instance = _instance; - init_info.PhysicalDevice = _chosenGPU; - init_info.Device = _device; - init_info.Queue = _graphicsQueue; - init_info.DescriptorPool = imguiPool; - init_info.MinImageCount = 3; - init_info.ImageCount = 3; - init_info.UseDynamicRendering = true; + ImGui_ImplVulkan_InitInfo init_info = {.Instance = engine._instance, + .PhysicalDevice = engine._chosenGPU, + .Device = engine._device, + .Queue = engine._graphicsQueue, + .DescriptorPool = _imguiPool, + .MinImageCount = 3, + .ImageCount = 3, + .UseDynamicRendering = true}; // dynamic rendering parameters for imgui to use init_info.PipelineRenderingCreateInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO}; init_info.PipelineRenderingCreateInfo.colorAttachmentCount = 1; init_info.PipelineRenderingCreateInfo.pColorAttachmentFormats = - &_swapchainImageFormat; + &engine._swapchainImageFormat; init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; ImGui_ImplVulkan_Init(&init_info); ImGui_ImplVulkan_CreateFontsTexture(); +} - // Store ImGui cleanup info - will be automatically handled when engine destructs - // Note: ImGui cleanup is now handled by storing the descriptor pool - // that will be automatically destroyed when the device is destroyed +VulkanEngine::Imgui::~Imgui() { + vkDestroyDescriptorPool(_device, _imguiPool, nullptr); + ImGui_ImplVulkan_Shutdown(); +} + + +void VulkanEngine::init_imgui() { + _imgui = std::make_unique(*this); } void VulkanEngine::init_descriptors() { @@ -617,7 +618,6 @@ void VulkanEngine::cleanup() { vkb::destroy_debug_utils_messenger(_instance, _debug_messenger); vkDestroyInstance(_instance, nullptr); - // VMA allocator cleanup vmaDestroyAllocator(_allocator); } diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 40db0c65..3e3e656e 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -181,6 +181,15 @@ class VulkanEngine { VmaMemoryUsage memoryUsage) const; private: + struct Imgui { + explicit Imgui(const VulkanEngine& engine); + ~Imgui(); + private: + VkDescriptorPool _imguiPool; + void initImguiPool(const VkDevice& device); + const VkDevice _device; + }; + std::unique_ptr _imgui; // Smart pointer collections for automatic cleanup std::vector> _managedBuffers; std::vector> _managedImages; From fd279664c32f6bdb8d8ecd8f8cabdc771c367856 Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:31:18 +0300 Subject: [PATCH 04/24] chore: cleanup -> dtor --- src/core/ModelImpl.cpp | 4 ---- src/graphics/vulkan/vk_engine.cpp | 7 ++----- src/include/core/ModelImpl.h | 1 - src/include/graphics/vulkan/vk_engine.h | 5 +---- 4 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index f8e28caa..35d4dd9d 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -4,10 +4,6 @@ #include "core/Mesh.h" -ModelImpl::~ModelImpl() { - _engine.cleanup(); -} - ModelImpl::ModelImpl() = default; void ModelImpl::registerWindow(SDL_Window *window) { diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index b87ab47b..f0f244ee 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -590,7 +590,8 @@ void VulkanEngine::destroy_swapchain() { } } -void VulkanEngine::cleanup() { + VulkanEngine::~VulkanEngine() + { if (_isInitialized) { // make sure the gpu has stopped doing its things vkDeviceWaitIdle(_device); @@ -1095,10 +1096,6 @@ AllocatedImage VulkanEngine::create_image(const void* data, VkExtent3D size, return new_image; } -void VulkanEngine::destroy_image(const AllocatedImage& img) const { - vkDestroyImageView(_device, img.imageView, nullptr); - vmaDestroyImage(_allocator, img.image, img.allocation); -} void MeshNode::Draw(const glm::mat4& topMatrix, DrawContext& ctx) { const glm::mat4 nodeMatrix = topMatrix * worldTransform; diff --git a/src/include/core/ModelImpl.h b/src/include/core/ModelImpl.h index 2f2b6dc9..6dc60c0e 100644 --- a/src/include/core/ModelImpl.h +++ b/src/include/core/ModelImpl.h @@ -17,7 +17,6 @@ class ModelImpl : public IModel { * throws std::runtime_error() */ ModelImpl(); - ~ModelImpl() override; ModelImpl(const ModelImpl &) = delete; ModelImpl &operator=(const ModelImpl &) = delete; diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 3e3e656e..b2d565fc 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -108,9 +108,6 @@ class VulkanEngine { // initializes everything in the engine void init(struct SDL_Window* window); - // shuts down the engine - void cleanup(); - // draw loop void draw(); @@ -179,7 +176,7 @@ class VulkanEngine { AllocatedBuffer create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage) const; - + ~VulkanEngine(); private: struct Imgui { explicit Imgui(const VulkanEngine& engine); From c1796917d35e9e4c21c6ff5a05cd69a894a201cd Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:32:24 +0300 Subject: [PATCH 05/24] fix: memory leak --- demo/main.cpp | 2 +- include/IController.h | 2 +- src/core/Controller.cpp | 4 +- src/core/ControllerImpl.cpp | 53 +++++- src/core/Mesh.cpp | 14 +- src/core/ModelImpl.cpp | 14 +- src/core/View.cpp | 4 +- src/core/ViewImpl.cpp | 100 ++--------- src/core/createInstance.cpp | 4 +- src/graphics/vulkan/pipelines.cpp | 14 +- src/graphics/vulkan/vk_command_buffers.cpp | 16 +- .../vulkan/vk_command_buffers_container.cpp | 18 +- src/graphics/vulkan/vk_engine.cpp | 164 +++++++++--------- src/graphics/vulkan/vk_loader.cpp | 30 +++- src/include/core/Controller.h | 2 +- src/include/core/ControllerImpl.h | 10 +- src/include/core/Mesh.h | 3 - src/include/core/ModelImpl.h | 9 +- src/include/core/View.h | 2 +- src/include/core/ViewImpl.h | 14 +- src/include/graphics/vulkan/vk_engine.h | 85 ++++++--- .../graphics/vulkan/vk_smart_wrappers.h | 6 +- src/include/interfaces/IModel.h | 29 +--- src/include/interfaces/IView.h | 20 +-- 24 files changed, 291 insertions(+), 328 deletions(-) diff --git a/demo/main.cpp b/demo/main.cpp index 4585d801..7815afde 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -7,7 +7,7 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] char* args[]) { try { const auto controller = createInstance(); - controller->init(); + controller->run(); } catch (const std::runtime_error& e) { std::cerr << "Unhandled exception: " << e.what() << '\n'; } diff --git a/include/IController.h b/include/IController.h index c469b8bb..7a4de99f 100644 --- a/include/IController.h +++ b/include/IController.h @@ -29,7 +29,7 @@ class IController { * * This method initializes the necessary components for the controller. */ - virtual void init() const = 0; + virtual void run() const = 0; /*! * \brief Updates the controller status. diff --git a/src/core/Controller.cpp b/src/core/Controller.cpp index cfabd7e7..e2143282 100644 --- a/src/core/Controller.cpp +++ b/src/core/Controller.cpp @@ -5,6 +5,6 @@ #include "core/ControllerImpl.h" -IController::Ptr createController(IModel::Ptr ptr) { - return std::make_shared(std::move(ptr)); +IController::Ptr createController() { + return std::make_shared(); } \ No newline at end of file diff --git a/src/core/ControllerImpl.cpp b/src/core/ControllerImpl.cpp index d7ee3a5f..0aeb0f08 100644 --- a/src/core/ControllerImpl.cpp +++ b/src/core/ControllerImpl.cpp @@ -3,15 +3,19 @@ #include #include #include +#include #include +#include #define GLM_ENABLE_EXPERIMENTAL #include +#include +#include "core/Model.h" #include "core/View.h" #include "scene/Camera.h" -ControllerImpl::ControllerImpl(IModel::Ptr model) : _model(std::move(model)) {} +ControllerImpl::ControllerImpl() : _view(createView()), _model(createModel()) {} double getCurrentGlobalTime() { // Get the current time point @@ -56,8 +60,47 @@ void ControllerImpl::processEvent(SDL_Event &e) const { _model->getCamera()->processSDLEvent(e); } -void ControllerImpl::init() const { - const auto controller = shared_from_this(); - const auto view = createView(controller, _model); - view->run(); +void ControllerImpl::createCubes() const { + for (int i = 0; i < 5; i++) { + _model->createMesh("cube" + std::to_string(i)); + } +} + +void ControllerImpl::run() const { + createCubes(); + + SDL_Event e; + bool bQuit = false; + bool stop_rendering = false; + + // main loop + while (!bQuit) { + // Handle events on queue + while (SDL_PollEvent(&e) != 0) { + // close the window when user alt-f4s or clicks the X button + if (e.type == SDL_QUIT) bQuit = true; + + if (e.type == SDL_WINDOWEVENT) { + if (e.window.event == SDL_WINDOWEVENT_MINIMIZED) { + stop_rendering = true; + } + if (e.window.event == SDL_WINDOWEVENT_RESTORED) { + stop_rendering = false; + } + } + + processEvent(e); + ImGui_ImplSDL2_ProcessEvent(&e); + } + + // do not draw if we are minimized + if (stop_rendering) { + // throttle the speed to avoid the endless spinning + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + continue; + } + + _view->render(); + update(); + } } diff --git a/src/core/Mesh.cpp b/src/core/Mesh.cpp index f496c69d..6536b3d8 100644 --- a/src/core/Mesh.cpp +++ b/src/core/Mesh.cpp @@ -1,15 +1,13 @@ // Mesh.cpp #include "core/Mesh.h" +#include "graphics/vulkan/vk_engine.h" + Mesh::Mesh(const std::string &filePath) : _currentModelPath(filePath) { VulkanEngine &engine = VulkanEngine::Get(); _rid = engine.registerMesh(filePath); } -Mesh::~Mesh() { - remove_model(); -} - void Mesh::set_model(const std::string &filePath) { VulkanEngine &engine = VulkanEngine::Get(); @@ -26,14 +24,6 @@ void Mesh::set_model(const std::string &filePath) { engine.setMeshTransform(_rid, _transform); } -void Mesh::remove_model() { - if (!_currentModelPath.empty()) { - VulkanEngine &engine = VulkanEngine::Get(); - engine.unregisterMesh(_rid); - _currentModelPath.clear(); - } -} - void Mesh::set_transform(glm::mat4 t) { VulkanEngine &engine = VulkanEngine::Get(); diff --git a/src/core/ModelImpl.cpp b/src/core/ModelImpl.cpp index 35d4dd9d..1258b1f7 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -1,18 +1,12 @@ #include "core/ModelImpl.h" - -#include - #include "core/Mesh.h" +#include "graphics/vulkan/vk_engine.h" -ModelImpl::ModelImpl() = default; - -void ModelImpl::registerWindow(SDL_Window *window) { - _engine.mainCamera = &_camera; - _engine.init(window); -} +ModelImpl::ModelImpl() + : _camera(), _engine(std::make_shared(_camera)) {}; void ModelImpl::updateVulkan() { - _engine.update(); + _engine->update(); } Camera *ModelImpl::getCamera() { diff --git a/src/core/View.cpp b/src/core/View.cpp index 8751d9d1..70513d67 100644 --- a/src/core/View.cpp +++ b/src/core/View.cpp @@ -5,6 +5,6 @@ #include "core/ViewImpl.h" -IView::Ptr createView(IController::Ptr controller, IModel::Ptr model) { - return std::make_unique(std::move(controller), std::move(model)); +IView::Ptr createView() { + return std::make_unique(); } \ No newline at end of file diff --git a/src/core/ViewImpl.cpp b/src/core/ViewImpl.cpp index 45fc8b76..0e2bab3e 100644 --- a/src/core/ViewImpl.cpp +++ b/src/core/ViewImpl.cpp @@ -2,101 +2,33 @@ #include #include -#include - -#ifdef __GNUC__ -#include -#else -#include -#endif - -#include -#include -#include -#include #include "graphics/vulkan/vk_engine.h" #include "imgui.h" #include "imgui_impl_sdl2.h" #include "imgui_impl_vulkan.h" -namespace { -constexpr auto kWindowFlags = - static_cast(SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE); -} - -ViewImpl::ViewImpl(IController::Ptr controller, IModel::Ptr model) - : _controller(std::move(controller)), _model(std::move(model)) { +ViewImpl::ViewImpl() { SDL_Init(SDL_INIT_VIDEO); - window = SDL_CreateWindow("engine", SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, 1700, 900, kWindowFlags); } -void createCubes(const std::shared_ptr &_model) { - for (int i = 0; i < 5; i++) { - _model->createMesh("cube" + std::to_string(i)); - } -} - -void ViewImpl::run() const { - _model->registerWindow(window); - - createCubes(_model); - - SDL_Event e; - bool bQuit = false; +struct EventAdapter final : Event { + explicit EventAdapter(const SDL_Event& event) : event(event) {} + SDL_Event event; +}; - bool stop_rendering = false; +void ViewImpl::render() { + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplSDL2_NewFrame(); + ImGui::NewFrame(); - // main loop - while (!bQuit) { - // Handle events on queue - while (SDL_PollEvent(&e) != 0) { - // close the window when user alt-f4s or clicks the X button - if (e.type == SDL_QUIT) bQuit = true; - - if (e.type == SDL_WINDOWEVENT) { - if (e.window.event == SDL_WINDOWEVENT_MINIMIZED) { - stop_rendering = true; - } - if (e.window.event == SDL_WINDOWEVENT_RESTORED) { - stop_rendering = false; - } - } - - _controller->processEvent(e); - ImGui_ImplSDL2_ProcessEvent(&e); - } - - // do not draw if we are minimized - if (stop_rendering) { - // throttle the speed to avoid the endless spinning - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - continue; - } - - // imgui new frame - ImGui_ImplVulkan_NewFrame(); - ImGui_ImplSDL2_NewFrame(); - ImGui::NewFrame(); - - if (ImGui::Begin("background")) { - VulkanEngine &engine = VulkanEngine::Get(); - ImGui::SliderFloat("Render Scale", &engine.renderScale, 0.3f, 1.f); - // other code - } - ImGui::End(); - - // make imgui calculate internal draw structures - ImGui::Render(); - - _controller->update(); - //_model->updateVulkan(); - - // engine.update(); + if (ImGui::Begin("background")) { + VulkanEngine &engine = VulkanEngine::Get(); + ImGui::SliderFloat("Render Scale", &engine.renderScale, 0.3f, 1.f); + // other code } -} + ImGui::End(); -ViewImpl::~ViewImpl() { - SDL_DestroyWindow(window); + // make imgui calculate internal draw structures + ImGui::Render(); } diff --git a/src/core/createInstance.cpp b/src/core/createInstance.cpp index fe1eb2bb..eb69fadf 100644 --- a/src/core/createInstance.cpp +++ b/src/core/createInstance.cpp @@ -3,7 +3,7 @@ #include "core/Model.h" IController::Ptr createInstance() { - const auto model = createModel(); - const auto controller = createController(model); + + const auto controller = createController(); return controller; } diff --git a/src/graphics/vulkan/pipelines.cpp b/src/graphics/vulkan/pipelines.cpp index 5926226b..c1470b90 100644 --- a/src/graphics/vulkan/pipelines.cpp +++ b/src/graphics/vulkan/pipelines.cpp @@ -17,15 +17,15 @@ void GLTFMetallic_Roughness::build_pipelines(VulkanEngine* engine) { build_transparent_pipeline(engine, meshVertexShader, meshFragShader, newLayout); - vkDestroyShaderModule(engine->_device, meshFragShader, nullptr); - vkDestroyShaderModule(engine->_device, meshVertexShader, nullptr); + vkDestroyShaderModule(engine->getRawDevice(), meshFragShader, nullptr); + vkDestroyShaderModule(engine->getRawDevice(), meshVertexShader, nullptr); } VkShaderModule GLTFMetallic_Roughness::load_shader(VulkanEngine* engine, const char* relative_path, const char* type) { VkShaderModule shaderModule; - if (!vkutil::load_shader_module(relative_path, engine->_device, + if (!vkutil::load_shader_module(relative_path, engine->getRawDevice(), &shaderModule)) { fmt::println("Error when building the {} shader module", type); } @@ -39,7 +39,7 @@ void GLTFMetallic_Roughness::create_material_layout(VulkanEngine* engine) { layoutBuilder.add_binding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); materialLayout = layoutBuilder.build( - engine->_device, + engine->getRawDevice(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT); } @@ -60,7 +60,7 @@ VkPipelineLayout GLTFMetallic_Roughness::create_pipeline_layout( mesh_layout_info.pushConstantRangeCount = 1; VkPipelineLayout newLayout; - VK_CHECK(vkCreatePipelineLayout(engine->_device, &mesh_layout_info, nullptr, + VK_CHECK(vkCreatePipelineLayout(engine->getRawDevice(), &mesh_layout_info, nullptr, &newLayout)); return newLayout; @@ -82,7 +82,7 @@ void GLTFMetallic_Roughness::build_opaque_pipeline(VulkanEngine* engine, pipelineBuilder.set_depth_format(engine->_depthImage->get().imageFormat); pipelineBuilder._pipelineLayout = layout; - opaquePipeline.pipeline = pipelineBuilder.build_pipeline(engine->_device); + opaquePipeline.pipeline = pipelineBuilder.build_pipeline(engine->getRawDevice()); } void GLTFMetallic_Roughness::build_transparent_pipeline( @@ -95,7 +95,7 @@ void GLTFMetallic_Roughness::build_transparent_pipeline( pipelineBuilder._pipelineLayout = layout; transparentPipeline.pipeline = - pipelineBuilder.build_pipeline(engine->_device); + pipelineBuilder.build_pipeline(engine->getRawDevice()); } MaterialInstance GLTFMetallic_Roughness::write_material( diff --git a/src/graphics/vulkan/vk_command_buffers.cpp b/src/graphics/vulkan/vk_command_buffers.cpp index 9114b9ed..9a748dbe 100644 --- a/src/graphics/vulkan/vk_command_buffers.cpp +++ b/src/graphics/vulkan/vk_command_buffers.cpp @@ -13,31 +13,31 @@ void CommandBuffers::init_commands(VulkanEngine* vk_engine) { for (auto& _frame : vk_engine->command_buffers_container._frames) { VkCommandPool commandPool; - VK_CHECK(vkCreateCommandPool(vk_engine->_device, &commandPoolInfo, + VK_CHECK(vkCreateCommandPool(vk_engine->getRawDevice(), &commandPoolInfo, nullptr, &commandPool)); - _frame._commandPool = std::make_unique(vk_engine->_device, commandPool); + _frame._commandPool = std::make_unique(vk_engine->getRawDevice(), commandPool); // allocate the default command buffer that we will use for rendering VkCommandBufferAllocateInfo cmdAllocInfo = vkinit::command_buffer_allocate_info(_frame._commandPool->get(), 1); - VK_CHECK(vkAllocateCommandBuffers(vk_engine->_device, &cmdAllocInfo, + VK_CHECK(vkAllocateCommandBuffers(vk_engine->getRawDevice(), &cmdAllocInfo, &_frame._mainCommandBuffer)); } VkCommandPool immCommandPool; - VK_CHECK(vkCreateCommandPool(vk_engine->_device, &commandPoolInfo, nullptr, + VK_CHECK(vkCreateCommandPool(vk_engine->getRawDevice(), &commandPoolInfo, nullptr, &immCommandPool)); - vk_engine->command_buffers_container._immCommandPool = std::make_unique(vk_engine->_device, immCommandPool); + vk_engine->command_buffers_container._immCommandPool = std::make_unique(vk_engine->getRawDevice(), immCommandPool); // allocate the command buffer for immediate submits const VkCommandBufferAllocateInfo cmdAllocInfo = vkinit::command_buffer_allocate_info(vk_engine->command_buffers_container._immCommandPool->get(), 1); - VK_CHECK(vkAllocateCommandBuffers(vk_engine->_device, &cmdAllocInfo, + VK_CHECK(vkAllocateCommandBuffers(vk_engine->getRawDevice(), &cmdAllocInfo, &(vk_engine->command_buffers_container._immCommandBuffer))); // Smart pointer will automatically handle cleanup - no need for deletion queue @@ -46,7 +46,7 @@ void CommandBuffers::init_commands(VulkanEngine* vk_engine) { void CommandBuffers::immediate_submit( std::function&& function, VulkanEngine* vk_engine) const { - VK_CHECK(vkResetFences(vk_engine->_device, 1, vk_engine->command_buffers_container._immFence->getPtr())); + VK_CHECK(vkResetFences(vk_engine->getRawDevice(), 1, vk_engine->command_buffers_container._immFence->getPtr())); VK_CHECK(vkResetCommandBuffer(vk_engine->command_buffers_container._immCommandBuffer, 0)); const VkCommandBuffer cmd = vk_engine->command_buffers_container._immCommandBuffer; @@ -71,6 +71,6 @@ void CommandBuffers::immediate_submit( VK_CHECK(vkQueueSubmit2(vk_engine->_graphicsQueue, 1, &submit, vk_engine->command_buffers_container._immFence->get())); - VK_CHECK(vkWaitForFences(vk_engine->_device, 1, vk_engine->command_buffers_container._immFence->getPtr(), true, + VK_CHECK(vkWaitForFences(vk_engine->getRawDevice(), 1, vk_engine->command_buffers_container._immFence->getPtr(), true, 9999999999)); } diff --git a/src/graphics/vulkan/vk_command_buffers_container.cpp b/src/graphics/vulkan/vk_command_buffers_container.cpp index c1abcc6c..8a5b48af 100644 --- a/src/graphics/vulkan/vk_command_buffers_container.cpp +++ b/src/graphics/vulkan/vk_command_buffers_container.cpp @@ -15,18 +15,18 @@ void CommandBuffersContainer::init_sync_structures(VulkanEngine* vk_engine) { for (auto& _frame : _frames) { VkFence renderFence; - VK_CHECK(vkCreateFence(vk_engine->_device, &fenceCreateInfo, nullptr, &renderFence)); - _frame._renderFence = std::make_unique(vk_engine->_device, renderFence); + VK_CHECK(vkCreateFence(vk_engine->getRawDevice(), &fenceCreateInfo, nullptr, &renderFence)); + _frame._renderFence = std::make_unique(vk_engine->getRawDevice(), renderFence); VkSemaphore swapchainSemaphore, renderSemaphore; - VK_CHECK(vkCreateSemaphore(vk_engine->_device, &semaphoreCreateInfo, nullptr, &swapchainSemaphore)); - VK_CHECK(vkCreateSemaphore(vk_engine->_device, &semaphoreCreateInfo, nullptr, &renderSemaphore)); - - _frame._swapchainSemaphore = std::make_unique(vk_engine->_device, swapchainSemaphore); - _frame._renderSemaphore = std::make_unique(vk_engine->_device, renderSemaphore); + VK_CHECK(vkCreateSemaphore(vk_engine->getRawDevice(), &semaphoreCreateInfo, nullptr, &swapchainSemaphore)); + VK_CHECK(vkCreateSemaphore(vk_engine->getRawDevice(), &semaphoreCreateInfo, nullptr, &renderSemaphore)); + + _frame._swapchainSemaphore = std::make_unique(vk_engine->getRawDevice(), swapchainSemaphore); + _frame._renderSemaphore = std::make_unique(vk_engine->getRawDevice(), renderSemaphore); } VkFence immFence; - VK_CHECK(vkCreateFence(vk_engine->_device, &fenceCreateInfo, nullptr, &immFence)); - _immFence = std::make_unique(vk_engine->_device, immFence); + VK_CHECK(vkCreateFence(vk_engine->getRawDevice(), &fenceCreateInfo, nullptr, &immFence)); + _immFence = std::make_unique(vk_engine->getRawDevice(), immFence); } \ No newline at end of file diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index f0f244ee..f5619775 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -19,6 +19,7 @@ #include "graphics/vulkan/vk_descriptors.h" #include "scene/Camera.h" + #define VMA_IMPLEMENTATION #include #include @@ -98,6 +99,11 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanEngine::debugCallback( return VK_FALSE; } +void VulkanEngine::destroy_image(const AllocatedImage& img) const { + vkDestroyImageView(getRawDevice(), img.imageView, nullptr); + vmaDestroyImage(_a._allocator, img.image, img.allocation); +} + void VulkanEngine::init_default_data() { std::array rect_vertices{}; @@ -124,17 +130,17 @@ void VulkanEngine::init_default_data() { const uint32_t white = glm::packUnorm4x8(glm::vec4(1, 1, 1, 1)); AllocatedImage whiteImageData = create_image(&white, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - _whiteImage = std::make_unique(_allocator, _device, whiteImageData); + _whiteImage = std::make_unique(_a._allocator, getRawDevice(), whiteImageData); const uint32_t grey = glm::packUnorm4x8(glm::vec4(0.66f, 0.66f, 0.66f, 1)); AllocatedImage greyImageData = create_image(&grey, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - _greyImage = std::make_unique(_allocator, _device, greyImageData); + _greyImage = std::make_unique(_a._allocator, getRawDevice(), greyImageData); const uint32_t black = glm::packUnorm4x8(glm::vec4(0, 0, 0, 0)); AllocatedImage blackImageData = create_image(&black, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - _blackImage = std::make_unique(_allocator, _device, blackImageData); + _blackImage = std::make_unique(_a._allocator, getRawDevice(), blackImageData); // checkerboard image const uint32_t magenta = glm::packUnorm4x8(glm::vec4(1, 0, 1, 1)); @@ -146,7 +152,7 @@ void VulkanEngine::init_default_data() { } AllocatedImage errorImageData = create_image(pixels.data(), VkExtent3D{16, 16, 1}, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - _errorCheckerboardImage = std::make_unique(_allocator, _device, errorImageData); + _errorCheckerboardImage = std::make_unique(_a._allocator, getRawDevice(), errorImageData); VkSamplerCreateInfo sampl = {.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; @@ -154,11 +160,11 @@ void VulkanEngine::init_default_data() { sampl.magFilter = VK_FILTER_NEAREST; sampl.minFilter = VK_FILTER_NEAREST; - vkCreateSampler(_device, &sampl, nullptr, &_defaultSamplerNearest); + vkCreateSampler(getRawDevice(), &sampl, nullptr, &_defaultSamplerNearest); sampl.magFilter = VK_FILTER_LINEAR; sampl.minFilter = VK_FILTER_LINEAR; - vkCreateSampler(_device, &sampl, nullptr, &_defaultSamplerLinear); + vkCreateSampler(getRawDevice(), &sampl, nullptr, &_defaultSamplerLinear); GLTFMetallic_Roughness::MaterialResources materialResources{}; // default the material textures @@ -180,17 +186,17 @@ void VulkanEngine::init_default_data() { sceneUniformData->metal_rough_factors = glm::vec4{1, 0.5, 0, 0}; // Store material constants buffer in managed buffers for automatic cleanup - _managedBuffers.push_back(std::make_unique(_allocator, materialConstants)); + _managedBuffers.push_back(std::make_unique(_a._allocator, materialConstants)); materialResources.dataBuffer = materialConstants.buffer; materialResources.dataBufferOffset = 0; defaultData = metalRoughMaterial.write_material( - _device, MaterialPass::MainColor, materialResources, + getRawDevice(), MaterialPass::MainColor, materialResources, globalDescriptorAllocator); } - void VulkanEngine::Imgui::initImguiPool(const VkDevice& device) { + void VulkanEngine::Imgui::initImguiPool() { // 1: create descriptor pool for IMGUI // the size of the pool is very oversize, but it's copied from imgui demo // itself. @@ -211,21 +217,24 @@ void VulkanEngine::init_default_data() { .maxSets = 1000, .poolSizeCount = (uint32_t)std::size(pool_sizes), .pPoolSizes = pool_sizes}; - VK_CHECK(vkCreateDescriptorPool(device, &pool_info, nullptr, &_imguiPool)); + VK_CHECK(vkCreateDescriptorPool(_device, &pool_info, nullptr, &_imguiPool)); } - VulkanEngine::Imgui::Imgui(const VulkanEngine& engine) : _imguiPool{}, _device{engine._device} { - initImguiPool(engine._device); + void VulkanEngine::Imgui::init(const VkDevice& dev, SDL_Window* w, + const VkInstance& instance, const VkPhysicalDevice& physicalDevice, + const VkQueue& queue, const VkFormat* format) { + _device = dev; + initImguiPool(); // 2: initialize imgui library // this initializes the core structures of imgui ImGui::CreateContext(); // this initializes imgui for SDL - ImGui_ImplSDL2_InitForVulkan(engine._window); + ImGui_ImplSDL2_InitForVulkan(w); // this initializes imgui for Vulkan - ImGui_ImplVulkan_InitInfo init_info = {.Instance = engine._instance, - .PhysicalDevice = engine._chosenGPU, - .Device = engine._device, - .Queue = engine._graphicsQueue, + ImGui_ImplVulkan_InitInfo init_info = {.Instance = instance, + .PhysicalDevice = physicalDevice, + .Device = _device, + .Queue = queue, .DescriptorPool = _imguiPool, .MinImageCount = 3, .ImageCount = 3, @@ -236,7 +245,7 @@ void VulkanEngine::init_default_data() { .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO}; init_info.PipelineRenderingCreateInfo.colorAttachmentCount = 1; init_info.PipelineRenderingCreateInfo.pColorAttachmentFormats = - &engine._swapchainImageFormat; + format; init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; @@ -244,38 +253,33 @@ void VulkanEngine::init_default_data() { ImGui_ImplVulkan_CreateFontsTexture(); } - +#include VulkanEngine::Imgui::~Imgui() { vkDestroyDescriptorPool(_device, _imguiPool, nullptr); ImGui_ImplVulkan_Shutdown(); } - -void VulkanEngine::init_imgui() { - _imgui = std::make_unique(*this); -} - void VulkanEngine::init_descriptors() { // create a descriptor pool that will hold 10 sets with 1 image each std::vector sizes = { {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1}, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1}}; - globalDescriptorAllocator.init(_device, 10, sizes); + globalDescriptorAllocator.init(getRawDevice(), 10, sizes); // make the descriptor set layout for our compute draw { DescriptorLayoutBuilder builder; builder.add_binding(0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE); _drawImageDescriptorLayout = - builder.build(_device, VK_SHADER_STAGE_COMPUTE_BIT); + builder.build(getRawDevice(), VK_SHADER_STAGE_COMPUTE_BIT); } { DescriptorLayoutBuilder builder; builder.add_binding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); _gpuSceneDataDescriptorLayout = - builder.build(_device, VK_SHADER_STAGE_VERTEX_BIT | + builder.build(getRawDevice(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT); } @@ -283,19 +287,19 @@ void VulkanEngine::init_descriptors() { DescriptorLayoutBuilder builder; builder.add_binding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); _singleImageDescriptorLayout = - builder.build(_device, VK_SHADER_STAGE_FRAGMENT_BIT); + builder.build(getRawDevice(), VK_SHADER_STAGE_FRAGMENT_BIT); } // allocate a descriptor set for our draw image _drawImageDescriptors = globalDescriptorAllocator.allocate( - _device, _drawImageDescriptorLayout); + getRawDevice(), _drawImageDescriptorLayout); DescriptorWriter writer; writer.write_image(0, _drawImage->imageView(), VK_NULL_HANDLE, VK_IMAGE_LAYOUT_GENERAL, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE); - writer.update_set(_device, _drawImageDescriptors); + writer.update_set(getRawDevice(), _drawImageDescriptors); for (auto& _frame : command_buffers_container._frames) { // create a descriptor pool @@ -307,33 +311,31 @@ void VulkanEngine::init_descriptors() { }; _frame._frameDescriptors = DescriptorAllocatorGrowable{}; - _frame._frameDescriptors.init(_device, 1000, frame_sizes); + _frame._frameDescriptors.init(getRawDevice(), 1000, frame_sizes); // No need for deletion queue - frame descriptors will be cleaned up in cleanup() } } void VulkanEngine::init_pipelines() { - pipelines.init(_device, _singleImageDescriptorLayout, _drawImageDescriptorLayout, _drawImage->get()); + pipelines.init(getRawDevice(), _singleImageDescriptorLayout, _drawImageDescriptorLayout, _drawImage->get()); // Pipeline cleanup is handled automatically by the Pipelines object metalRoughMaterial.build_pipelines(this); } -void VulkanEngine::init(SDL_Window* window) { - _window = window; - +VulkanEngine::VulkanEngine(Camera& camera) : mainCamera(&camera) { // only one engine initialization is allowed with the application. assert(loadedEngine == nullptr); loadedEngine = this; init_vulkan(); init_swapchain(); - + command_buffers.init_commands(this); - + command_buffers_container.init_sync_structures(this); init_descriptors(); init_pipelines(); - init_imgui(); + _imgui.init(getRawDevice(), _window.ptr, _instance._instance, _chosenGPU, _graphicsQueue, &_swapchainImageFormat); init_default_data(); mainCamera->velocity = glm::vec3(0.f); @@ -390,10 +392,9 @@ void VulkanEngine::init_vulkan() { vkb::Instance vkb_inst = inst_ret.value(); // grab the instance - _instance = vkb_inst.instance; - _debug_messenger = vkb_inst.debug_messenger; + _instance.init(vkb_inst); - SDL_bool err = SDL_Vulkan_CreateSurface(_window, _instance, &_surface); + SDL_bool err = SDL_Vulkan_CreateSurface(_window.ptr, _instance._instance, &_surface); if (!err) { LOGE("Failed to create Vulkan surface. Error: {}", SDL_GetError()); } @@ -437,7 +438,7 @@ void VulkanEngine::init_vulkan() { const vkb::Device& vkbDevice = dev_ret.value(); // Get the VkDevice handle used in the rest of a vulkan application - _device = vkbDevice.device; + _device.init(vkbDevice); _chosenGPU = physicalDevice.physical_device; auto queue_ret = vkbDevice.get_queue(vkb::QueueType::graphics); @@ -459,17 +460,18 @@ void VulkanEngine::init_vulkan() { // initialize the memory allocator VmaAllocatorCreateInfo allocatorInfo = {}; allocatorInfo.physicalDevice = _chosenGPU; - allocatorInfo.device = _device; - allocatorInfo.instance = _instance; + allocatorInfo.device = getRawDevice(); + allocatorInfo.instance = _instance._instance; allocatorInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; - vmaCreateAllocator(&allocatorInfo, &_allocator); + // // vmaCreateAllocator(&allocatorInfo, &_a._allocator); + _a.init(allocatorInfo); // VMA allocator will be destroyed in cleanup() - no need for deletion queue } void VulkanEngine::create_swapchain(uint32_t width, uint32_t height) { - vkb::SwapchainBuilder swapchainBuilder{_chosenGPU, _device, _surface}; + vkb::SwapchainBuilder swapchainBuilder{_chosenGPU, getRawDevice(), _surface}; _swapchainImageFormat = VK_FORMAT_B8G8R8A8_UNORM; @@ -529,7 +531,7 @@ void VulkanEngine::init_swapchain() { drawImageData.imageExtent = drawImageExtent; // allocate and create the image - vmaCreateImage(_allocator, &rimg_info, &rimg_allocinfo, &drawImageData.image, + vmaCreateImage(_a._allocator, &rimg_info, &rimg_allocinfo, &drawImageData.image, &drawImageData.allocation, nullptr); // build an image-view for the draw image to use for rendering @@ -537,11 +539,11 @@ void VulkanEngine::init_swapchain() { drawImageFormat, drawImageData.image, VK_IMAGE_ASPECT_COLOR_BIT); - VK_CHECK(vkCreateImageView(_device, &rview_info, nullptr, + VK_CHECK(vkCreateImageView(getRawDevice(), &rview_info, nullptr, &drawImageData.imageView)); // Create smart pointer for automatic cleanup - _drawImage = std::make_unique(_allocator, _device, drawImageData); + _drawImage = std::make_unique(_a._allocator, getRawDevice(), drawImageData); // Create depth image VkExtent3D depthImageExtent = { @@ -567,26 +569,26 @@ void VulkanEngine::init_swapchain() { depthImageData.imageExtent = depthImageExtent; // allocate and create the depth image - vmaCreateImage(_allocator, &dimg_info, &dimg_allocinfo, &depthImageData.image, + vmaCreateImage(_a._allocator, &dimg_info, &dimg_allocinfo, &depthImageData.image, &depthImageData.allocation, nullptr); // build an image-view for the depth image VkImageViewCreateInfo dview_info = vkinit::imageview_create_info( depthFormat, depthImageData.image, VK_IMAGE_ASPECT_DEPTH_BIT); - VK_CHECK(vkCreateImageView(_device, &dview_info, nullptr, + VK_CHECK(vkCreateImageView(getRawDevice(), &dview_info, nullptr, &depthImageData.imageView)); // Create smart pointer for automatic cleanup - _depthImage = std::make_unique(_allocator, _device, depthImageData); + _depthImage = std::make_unique(_a._allocator, getRawDevice(), depthImageData); } void VulkanEngine::destroy_swapchain() { - vkDestroySwapchainKHR(_device, _swapchain, nullptr); + vkDestroySwapchainKHR(getRawDevice(), _swapchain, nullptr); // destroy swapchain resources for (const auto& _swapchainImageView : _swapchainImageViews) { - vkDestroyImageView(_device, _swapchainImageView, nullptr); + vkDestroyImageView(getRawDevice(), _swapchainImageView, nullptr); } } @@ -594,7 +596,7 @@ void VulkanEngine::destroy_swapchain() { { if (_isInitialized) { // make sure the gpu has stopped doing its things - vkDeviceWaitIdle(_device); + vkDeviceWaitIdle(getRawDevice()); loadedScenes.clear(); @@ -604,23 +606,17 @@ void VulkanEngine::destroy_swapchain() { // Smart pointers automatically clean up sync objects // Manual cleanup only for command pools and command buffers if (_frame._commandPool) { - vkDestroyCommandPool(_device, _frame._commandPool->get(), nullptr); + vkDestroyCommandPool(getRawDevice(), _frame._commandPool->get(), nullptr); } // Destroy frame descriptors manually - _frame._frameDescriptors.destroy_pools(_device); + _frame._frameDescriptors.destroy_pools(getRawDevice()); } - globalDescriptorAllocator.destroy_pools(_device); + globalDescriptorAllocator.destroy_pools(getRawDevice()); destroy_swapchain(); - vkDestroySurfaceKHR(_instance, _surface, nullptr); - vkDestroyDevice(_device, nullptr); - - vkb::destroy_debug_utils_messenger(_instance, _debug_messenger); - vkDestroyInstance(_instance, nullptr); - // VMA allocator cleanup - vmaDestroyAllocator(_allocator); + vkDestroySurfaceKHR(_instance._instance, _surface, nullptr); } // clear engine pointer @@ -644,7 +640,7 @@ AllocatedBuffer VulkanEngine::create_buffer(size_t allocSize, AllocatedBuffer newBuffer{}; // allocate the buffer - VK_CHECK(vmaCreateBuffer(_allocator, &bufferInfo, &vmallocinfo, + VK_CHECK(vmaCreateBuffer(_a._allocator, &bufferInfo, &vmallocinfo, &newBuffer.buffer, &newBuffer.allocation, &newBuffer.info)); @@ -652,7 +648,7 @@ AllocatedBuffer VulkanEngine::create_buffer(size_t allocSize, } void VulkanEngine::destroy_buffer(const AllocatedBuffer& buffer) const { - vmaDestroyBuffer(_allocator, buffer.buffer, buffer.allocation); + vmaDestroyBuffer(_a._allocator, buffer.buffer, buffer.allocation); } GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, @@ -675,7 +671,7 @@ GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, .buffer = newSurface.vertexBuffer.buffer}; newSurface.vertexBufferAddress = - vkGetBufferDeviceAddress(_device, &deviceAddressInfo); + vkGetBufferDeviceAddress(getRawDevice(), &deviceAddressInfo); // create index buffer newSurface.indexBuffer = create_buffer( @@ -714,8 +710,8 @@ GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, this); // Store mesh buffers in managed collections for automatic cleanup - _managedBuffers.push_back(std::make_unique(_allocator, newSurface.vertexBuffer)); - _managedBuffers.push_back(std::make_unique(_allocator, newSurface.indexBuffer)); + _managedBuffers.push_back(std::make_unique(_a._allocator, newSurface.vertexBuffer)); + _managedBuffers.push_back(std::make_unique(_a._allocator, newSurface.indexBuffer)); destroy_buffer(staging); return newSurface; @@ -758,7 +754,7 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { // add it to the current frame's managed buffers for automatic cleanup get_current_frame()._frameBuffers.push_back( - std::make_unique(_allocator, gpuSceneDataBuffer)); + std::make_unique(_a._allocator, gpuSceneDataBuffer)); // write the buffer auto* sceneUniformData = @@ -768,12 +764,12 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { // create a descriptor set that binds that buffer and update it VkDescriptorSet globalDescriptor = get_current_frame()._frameDescriptors.allocate( - _device, _gpuSceneDataDescriptorLayout); + getRawDevice(), _gpuSceneDataDescriptorLayout); DescriptorWriter writer; writer.write_buffer(0, gpuSceneDataBuffer.buffer, sizeof(GPUSceneData), 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); - writer.update_set(_device, globalDescriptor); + writer.update_set(getRawDevice(), globalDescriptor); VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info( _drawImage->imageView(), nullptr, VK_IMAGE_LAYOUT_GENERAL); @@ -805,13 +801,13 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { // bind a texture VkDescriptorSet imageSet = get_current_frame()._frameDescriptors.allocate( - _device, _singleImageDescriptorLayout); + getRawDevice(), _singleImageDescriptorLayout); DescriptorWriter single_image_writer; single_image_writer.write_image(0, _errorCheckerboardImage->imageView(), _defaultSamplerNearest, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); - single_image_writer.update_set(_device, imageSet); + single_image_writer.update_set(getRawDevice(), imageSet); pipelines.meshPipeline->bindDescriptorSets(cmd, &imageSet, 1); @@ -846,19 +842,19 @@ void VulkanEngine::draw() { // wait until the gpu has finished rendering the last frame. Timeout of 1 // second - VK_CHECK(vkWaitForFences(_device, 1, get_current_frame()._renderFence->getPtr(), + VK_CHECK(vkWaitForFences(getRawDevice(), 1, get_current_frame()._renderFence->getPtr(), true, 1000000000)); // Clear frame buffers instead of flushing deletion queue get_current_frame()._frameBuffers.clear(); - get_current_frame()._frameDescriptors.clear_pools(_device); + get_current_frame()._frameDescriptors.clear_pools(getRawDevice()); - VK_CHECK(vkResetFences(_device, 1, get_current_frame()._renderFence->getPtr())); + VK_CHECK(vkResetFences(getRawDevice(), 1, get_current_frame()._renderFence->getPtr())); // request image from the swapchain uint32_t swapchainImageIndex; const VkResult e = - vkAcquireNextImageKHR(_device, _swapchain, 1000000000, + vkAcquireNextImageKHR(getRawDevice(), _swapchain, 1000000000, get_current_frame()._swapchainSemaphore->get(), nullptr, &swapchainImageIndex); if (e == VK_ERROR_OUT_OF_DATE_KHR) { @@ -983,12 +979,12 @@ void VulkanEngine::draw() { } void VulkanEngine::resize_swapchain() { - vkDeviceWaitIdle(_device); + vkDeviceWaitIdle(getRawDevice()); destroy_swapchain(); int w, h; - SDL_GetWindowSize(_window, &w, &h); + SDL_GetWindowSize(_window.ptr, &w, &h); _windowExtent.width = static_cast(w); _windowExtent.height = static_cast(h); @@ -1026,7 +1022,7 @@ AllocatedImage VulkanEngine::create_image(VkExtent3D size, VkFormat format, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); // allocate and create the image - VK_CHECK(vmaCreateImage(_allocator, &img_info, &allocinfo, &newImage.image, + VK_CHECK(vmaCreateImage(_a._allocator, &img_info, &allocinfo, &newImage.image, &newImage.allocation, nullptr)); // if the format is a depth format, we will need to have it use the correct @@ -1041,7 +1037,7 @@ AllocatedImage VulkanEngine::create_image(VkExtent3D size, VkFormat format, vkinit::imageview_create_info(format, newImage.image, aspectFlag); view_info.subresourceRange.levelCount = img_info.mipLevels; - VK_CHECK(vkCreateImageView(_device, &view_info, nullptr, + VK_CHECK(vkCreateImageView(getRawDevice(), &view_info, nullptr, &newImage.imageView)); return newImage; @@ -1169,7 +1165,7 @@ int64_t VulkanEngine::registerMesh(const std::string& filePath) { } void VulkanEngine::unregisterMesh(int64_t id) { - if (meshes.find(id) != meshes.end()) { + if (meshes.contains(id)) { meshes.erase(id); transforms.erase(id); } diff --git a/src/graphics/vulkan/vk_loader.cpp b/src/graphics/vulkan/vk_loader.cpp index 5c00e7be..55169c22 100644 --- a/src/graphics/vulkan/vk_loader.cpp +++ b/src/graphics/vulkan/vk_loader.cpp @@ -251,7 +251,7 @@ std::optional> loadGltf(VulkanEngine* engine, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3}, {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}}; file.descriptorPool.init( - engine->_device, + engine->getRawDevice(), static_cast(std::max(gltf.materials.size(), size_t(1))), sizes); @@ -269,7 +269,7 @@ std::optional> loadGltf(VulkanEngine* engine, sampl.mipmapMode = extract_mipmap_mode( sampler.minFilter.value_or(fastgltf::Filter::Nearest)); VkSampler newSampler; - vkCreateSampler(engine->_device, &sampl, nullptr, &newSampler); + vkCreateSampler(engine->getRawDevice(), &sampl, nullptr, &newSampler); file.samplers.push_back(newSampler); } @@ -338,7 +338,7 @@ std::optional> loadGltf(VulkanEngine* engine, } newMat->data = engine->metalRoughMaterial.write_material( - engine->_device, passType, materialResources, + engine->getRawDevice(), passType, materialResources, file.descriptorPool); data_index++; } @@ -363,7 +363,7 @@ std::optional> loadGltf(VulkanEngine* engine, resources.dataBufferOffset = 0; defaultMat->data = engine->metalRoughMaterial.write_material( - engine->_device, MaterialPass::MainColor, resources, + engine->getRawDevice(), MaterialPass::MainColor, resources, file.descriptorPool); } @@ -523,4 +523,24 @@ void LoadedGLTF::Draw(const glm::mat4& topMatrix, DrawContext& ctx) { } } -void LoadedGLTF::clearAll() {} +void LoadedGLTF::clearAll() { + VkDevice dv = creator->getRawDevice(); + descriptorPool.destroy_pools(dv); + creator->destroy_buffer(materialDataBuffer); + // for (auto& [k, v] : meshes) { + // creator->destroy_buffer(v->meshBuffers.indexBuffer); + // creator->destroy_buffer(v->meshBuffers.vertexBuffer); + // } + for (auto& [k, v] : images) { + + if (v.image == creator->_errorCheckerboardImage.get()->image()) { + //dont destroy the default images + continue; + } + creator->destroy_image(v); + } + + for (auto& sampler : samplers) { + vkDestroySampler(dv, sampler, nullptr); + } +} diff --git a/src/include/core/Controller.h b/src/include/core/Controller.h index ed2e98c9..bb4b4102 100644 --- a/src/include/core/Controller.h +++ b/src/include/core/Controller.h @@ -3,4 +3,4 @@ #include "IController.h" #include "interfaces/IModel.h" -IController::Ptr createController(IModel::Ptr ptr); +IController::Ptr createController(); diff --git a/src/include/core/ControllerImpl.h b/src/include/core/ControllerImpl.h index f7025dd8..f9d75576 100644 --- a/src/include/core/ControllerImpl.h +++ b/src/include/core/ControllerImpl.h @@ -5,16 +5,18 @@ #include "IController.h" #include "interfaces/IModel.h" +#include "interfaces/IView.h" -class ControllerImpl : public IController, - public std::enable_shared_from_this { +class ControllerImpl : public IController { public: - explicit ControllerImpl(IModel::Ptr model); + ControllerImpl(); - void init() const override; + void run() const override; void update() const override; void processEvent(SDL_Event& e) const override; private: + std::shared_ptr _view; std::shared_ptr _model; + void createCubes() const; }; diff --git a/src/include/core/Mesh.h b/src/include/core/Mesh.h index a8e22857..4d204da0 100644 --- a/src/include/core/Mesh.h +++ b/src/include/core/Mesh.h @@ -4,16 +4,13 @@ #include #include "glm/detail/type_mat4x4.hpp" -#include "graphics/vulkan/vk_engine.h" class Mesh { public: Mesh(const std::string& filePath); - ~Mesh(); // Add model change functionality void set_model(const std::string& filePath); - void remove_model(); void set_transform(glm::mat4 t); glm::mat4 get_transform(); diff --git a/src/include/core/ModelImpl.h b/src/include/core/ModelImpl.h index 6dc60c0e..81d35538 100644 --- a/src/include/core/ModelImpl.h +++ b/src/include/core/ModelImpl.h @@ -5,10 +5,10 @@ #include #include -#include "graphics/vulkan/vk_engine.h" #include "interfaces/IModel.h" #include "scene/Camera.h" +class VulkanEngine; class Mesh; class ModelImpl : public IModel { @@ -16,12 +16,11 @@ class ModelImpl : public IModel { /*! \brief * throws std::runtime_error() */ - ModelImpl(); + explicit ModelImpl(); ModelImpl(const ModelImpl &) = delete; ModelImpl &operator=(const ModelImpl &) = delete; - void registerWindow(struct SDL_Window *window) override; void updateVulkan() override; void createMesh(std::string name) override; @@ -31,8 +30,6 @@ class ModelImpl : public IModel { private: std::unordered_map> _meshes; - - VulkanEngine _engine; - Camera _camera; + std::shared_ptr _engine; }; \ No newline at end of file diff --git a/src/include/core/View.h b/src/include/core/View.h index 73e9012c..033a7d43 100644 --- a/src/include/core/View.h +++ b/src/include/core/View.h @@ -4,4 +4,4 @@ #include "interfaces/IModel.h" #include "interfaces/IView.h" -IView::Ptr createView(IController::Ptr controller, IModel::Ptr model); \ No newline at end of file +IView::Ptr createView(); \ No newline at end of file diff --git a/src/include/core/ViewImpl.h b/src/include/core/ViewImpl.h index 1f599ab6..35c904a3 100644 --- a/src/include/core/ViewImpl.h +++ b/src/include/core/ViewImpl.h @@ -1,19 +1,9 @@ #pragma once -#include "IController.h" -#include "interfaces/IModel.h" #include "interfaces/IView.h" class ViewImpl : public IView { public: - ViewImpl(IController::Ptr controller, IModel::Ptr model); - ~ViewImpl() override; - - void run() const override; - -private: - IController::Ptr _controller; - IModel::Ptr _model; - - struct SDL_Window* window{nullptr}; + ViewImpl(); + void render() override; }; diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index b2d565fc..8b360bf3 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -1,5 +1,7 @@ #pragma once +#include +#include #include #include #include @@ -9,22 +11,21 @@ #include #include #include +#include #include #include #include -#include "vk_descriptors.h" -#include "vk_types.h" -#include "vk_smart_wrappers.h" - -#include "pipelines.h" #include "ComputePipeline.h" - +#include "core/ModelImpl.h" +#include "pipelines.h" #include "vk_command_buffers.h" #include "vk_command_buffers_container.h" +#include "vk_descriptors.h" +#include "vk_smart_wrappers.h" +#include "vk_types.h" class Camera; -class VulkanEngine; struct DrawContext; struct LoadedGLTF; struct MeshAsset; @@ -65,6 +66,39 @@ struct DrawContext { class VulkanEngine { public: + + struct Instance { + VkInstance _instance{nullptr}; // Vulkan library handle //todo: get + VkDebugUtilsMessengerEXT _debug_messenger{nullptr}; // Vulkan debug output handle + void init(const vkb::Instance& vkb_inst) { + _instance = vkb_inst.instance; + _debug_messenger = vkb_inst.debug_messenger; + } + ~Instance() { + if (_debug_messenger) { + vkb::destroy_debug_utils_messenger(_instance, _debug_messenger); + } + if (_instance) { + vkDestroyInstance(_instance, nullptr); + } + } + }; + Instance _instance; + + struct Device { + VkDevice _device{nullptr}; // Vulkan device for commands + void init(const vkb::Device& dev) { _device = dev.device; } + ~Device() {vkDestroyDevice(_device, nullptr);} + }; + Device _device; + struct Allocator { + VmaAllocator _allocator{nullptr}; + void init(VmaAllocatorCreateInfo info) {vmaCreateAllocator(&info, &_allocator);} // move info inside + ~Allocator() {vmaDestroyAllocator(_allocator);} + }; + Allocator _a; // todo: rename + VkDevice getRawDevice() const { return _device._device; } + Pipelines pipelines; CommandBuffers command_buffers; @@ -82,8 +116,6 @@ class VulkanEngine { std::unordered_map> loadedScenes; - Camera* mainCamera; - DrawContext mainDrawContext; std::unordered_map> loadedNodes; @@ -101,23 +133,17 @@ class VulkanEngine { bool stop_rendering{false}; VkExtent2D _windowExtent{2560, 1440}; - struct SDL_Window* _window{nullptr}; + Camera* mainCamera; static VulkanEngine& Get(); - // initializes everything in the engine - void init(struct SDL_Window* window); - // draw loop void draw(); // run main loop void update(); - VkInstance _instance; // Vulkan library handle - VkDebugUtilsMessengerEXT _debug_messenger; // Vulkan debug output handle VkPhysicalDevice _chosenGPU; // GPU chosen as the default device - VkDevice _device; // Vulkan device for commands VkSurfaceKHR _surface; // Vulkan window surface VkSwapchainKHR _swapchain; @@ -127,8 +153,6 @@ class VulkanEngine { std::vector _swapchainImageViews; VkExtent2D _swapchainExtent; - VmaAllocator _allocator; - std::unique_ptr _drawImage; std::unique_ptr _depthImage; VkExtent2D _drawExtent; @@ -176,17 +200,30 @@ class VulkanEngine { AllocatedBuffer create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage) const; + + // initializes everything in the engine + VulkanEngine(Camera& camera); + struct Window { + SDL_Window* ptr; + Window() : ptr{SDL_CreateWindow("engine", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, 1700, 900, SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE)} {} + ~Window() { SDL_DestroyWindow(ptr); } + }; + Window _window; ~VulkanEngine(); + void destroy_buffer(const AllocatedBuffer& buffer) const; private: struct Imgui { - explicit Imgui(const VulkanEngine& engine); + void init(const VkDevice& dev, SDL_Window* w, + const VkInstance& instance, const VkPhysicalDevice& physicalDevice, + const VkQueue& queue, const VkFormat* format); ~Imgui(); private: - VkDescriptorPool _imguiPool; - void initImguiPool(const VkDevice& device); - const VkDevice _device; + void initImguiPool(); + VkDevice _device{nullptr}; + VkDescriptorPool _imguiPool{nullptr}; }; - std::unique_ptr _imgui; + Imgui _imgui; // Smart pointer collections for automatic cleanup std::vector> _managedBuffers; std::vector> _managedImages; @@ -214,8 +251,6 @@ class VulkanEngine { void draw_geometry(VkCommandBuffer cmd); - void destroy_buffer(const AllocatedBuffer& buffer) const; - void resize_swapchain(); void init_mesh_pipeline(); diff --git a/src/include/graphics/vulkan/vk_smart_wrappers.h b/src/include/graphics/vulkan/vk_smart_wrappers.h index 568fddf3..46bad26c 100644 --- a/src/include/graphics/vulkan/vk_smart_wrappers.h +++ b/src/include/graphics/vulkan/vk_smart_wrappers.h @@ -97,9 +97,9 @@ class VulkanCommandPool { VulkanCommandPool(VkDevice device, VkCommandPool commandPool) : device_(device), commandPool_(commandPool) {} ~VulkanCommandPool() { - if (commandPool_ != VK_NULL_HANDLE) { - vkDestroyCommandPool(device_, commandPool_, nullptr); - } + // if (commandPool_ != VK_NULL_HANDLE) { + // vkDestroyCommandPool(device_, commandPool_, nullptr); + // } } // Move constructor and assignment diff --git a/src/include/interfaces/IModel.h b/src/include/interfaces/IModel.h index bdfc421c..e5c2516e 100644 --- a/src/include/interfaces/IModel.h +++ b/src/include/interfaces/IModel.h @@ -1,13 +1,8 @@ #pragma once -#include -#include #include #include -#include -#include "SDL2/SDL.h" -#include "SDL2/SDL_vulkan.h" #include "scene/Camera.h" /*! @@ -37,7 +32,7 @@ class IModel { * It is essential to call this method before performing any Vulkan * rendering operations. */ - virtual void registerWindow(struct SDL_Window* window) = 0; + // virtual void registerWindow(struct SDL_Window* window) = 0; /*! * \brief Updates Vulkan-related states. @@ -78,27 +73,5 @@ class IModel { */ [[nodiscard]] virtual Camera* getCamera() = 0; - /*! \brief - * Gets the chip handler from HIDAPI required to change the settings by the - * Controller - * @return - * Returns the device handler in case, can't be nullptr - */ - //[[nodiscard]] virtual hid_device* getChipHandler() const = 0; - - /*! \brief - * Requests from chip the level of brightness - * @return - * Returns the level of brightness [0; 100] - */ - // virtual uint8_t getBrightness() = 0; - - /*! \brief - * Requests RGB values from the chip - * @return - * Returns the struct with .R, .G, .B fields - */ - // virtual Color getRGB() = 0; - using Ptr = std::shared_ptr; }; diff --git a/src/include/interfaces/IView.h b/src/include/interfaces/IView.h index acde9d61..01089b0f 100644 --- a/src/include/interfaces/IView.h +++ b/src/include/interfaces/IView.h @@ -1,5 +1,11 @@ #pragma once +#include + +struct Event { + virtual ~Event() = default; +}; + /*! \brief * * Interface to show user their interaction abilities @@ -9,18 +15,6 @@ class IView { public: virtual ~IView() = default; - virtual void run() const = 0; - - /*! \brief - * Prints in console current settings - * (level of brightness and RGB) - */ - // virtual void showCurrentSettings() const = 0; - - /*! \brief - * Runs user interface that helps user interact with backlight - */ - // virtual void runMenu() const = 0; - + virtual void render() = 0; using Ptr = std::unique_ptr; }; From 250a81fdb0ae91aff121ddc1a3e8fdf74ca7d4ba Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:33:50 +0300 Subject: [PATCH 06/24] fix: memory leak --- src/include/graphics/vulkan/GraphicsPipeline.h | 4 +++- src/include/graphics/vulkan/pipelines.h | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/include/graphics/vulkan/GraphicsPipeline.h b/src/include/graphics/vulkan/GraphicsPipeline.h index 1e9a6f54..5907266c 100644 --- a/src/include/graphics/vulkan/GraphicsPipeline.h +++ b/src/include/graphics/vulkan/GraphicsPipeline.h @@ -40,7 +40,9 @@ class GraphicsPipeline : public IPipeline { const VkDescriptorSet* descriptorSets, uint32_t setCount, uint32_t firstSet = 0); void pushConstants(VkCommandBuffer cmd, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void* data); - + ~GraphicsPipeline() override { + GraphicsPipeline::destroy(); + } private: VkDevice _device = VK_NULL_HANDLE; VkPipeline _pipeline = VK_NULL_HANDLE; diff --git a/src/include/graphics/vulkan/pipelines.h b/src/include/graphics/vulkan/pipelines.h index a9229949..91be1a80 100644 --- a/src/include/graphics/vulkan/pipelines.h +++ b/src/include/graphics/vulkan/pipelines.h @@ -69,7 +69,6 @@ class Pipelines { VkDescriptorSetLayout drawImageDescriptorLayout, AllocatedImage drawImage); void destroy(); - private: VkDevice _device; VkDescriptorSetLayout _singleImageDescriptorLayout; From 835e4549e8a602c05417d7dfb8f03c68b01be236 Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:35:04 +0300 Subject: [PATCH 07/24] fix: memory leak Add dtors for ComputePipeline and MaterialPipeline --- src/graphics/vulkan/pipelines.cpp | 1 + src/include/graphics/vulkan/ComputePipeline.h | 4 +++- src/include/graphics/vulkan/vk_types.h | 9 +++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/graphics/vulkan/pipelines.cpp b/src/graphics/vulkan/pipelines.cpp index c1470b90..6af93108 100644 --- a/src/graphics/vulkan/pipelines.cpp +++ b/src/graphics/vulkan/pipelines.cpp @@ -83,6 +83,7 @@ void GLTFMetallic_Roughness::build_opaque_pipeline(VulkanEngine* engine, pipelineBuilder._pipelineLayout = layout; opaquePipeline.pipeline = pipelineBuilder.build_pipeline(engine->getRawDevice()); + opaquePipeline._device = engine->getRawDevice(); } void GLTFMetallic_Roughness::build_transparent_pipeline( diff --git a/src/include/graphics/vulkan/ComputePipeline.h b/src/include/graphics/vulkan/ComputePipeline.h index a88fe15c..924023d2 100644 --- a/src/include/graphics/vulkan/ComputePipeline.h +++ b/src/include/graphics/vulkan/ComputePipeline.h @@ -20,7 +20,9 @@ class ComputePipeline : public IPipeline { void init(VkDevice device) override; void bind(VkCommandBuffer cmd) override; void destroy() override; - + ~ComputePipeline() override { + ComputePipeline::destroy(); + } VkPipeline getPipeline() const override { return _pipeline; } VkPipelineLayout getLayout() const override { return _pipelineLayout; } diff --git a/src/include/graphics/vulkan/vk_types.h b/src/include/graphics/vulkan/vk_types.h index 172f637e..fd2b490a 100644 --- a/src/include/graphics/vulkan/vk_types.h +++ b/src/include/graphics/vulkan/vk_types.h @@ -65,6 +65,15 @@ enum class MaterialPass : uint8_t { MainColor, Transparent, Other }; struct MaterialPipeline { VkPipeline pipeline; VkPipelineLayout layout; + VkDevice _device = VK_NULL_HANDLE; + ~MaterialPipeline() { + if (_device != VK_NULL_HANDLE) { + vkDestroyPipelineLayout(_device, layout, nullptr); + vkDestroyPipeline(_device, pipeline, nullptr); + pipeline = VK_NULL_HANDLE; + layout = VK_NULL_HANDLE; + } + } }; struct MaterialInstance { From e2c4ad08818c09cc1e9c010a3a6c7f9211dbc4e2 Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:36:52 +0300 Subject: [PATCH 08/24] fix: memory leak Add wrapper for VkSampler --- src/graphics/vulkan/vk_engine.cpp | 11 +++++------ src/graphics/vulkan/vk_loader.cpp | 8 ++++---- src/include/graphics/vulkan/vk_engine.h | 21 ++++++++++++++++++--- 3 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index f5619775..83085651 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -159,19 +159,18 @@ void VulkanEngine::init_default_data() { sampl.magFilter = VK_FILTER_NEAREST; sampl.minFilter = VK_FILTER_NEAREST; - - vkCreateSampler(getRawDevice(), &sampl, nullptr, &_defaultSamplerNearest); + _defaultSamplerNearest.create(getRawDevice(), &sampl, nullptr); sampl.magFilter = VK_FILTER_LINEAR; sampl.minFilter = VK_FILTER_LINEAR; - vkCreateSampler(getRawDevice(), &sampl, nullptr, &_defaultSamplerLinear); + _defaultSamplerLinear.create(getRawDevice(), &sampl, nullptr); GLTFMetallic_Roughness::MaterialResources materialResources{}; // default the material textures materialResources.colorImage = _whiteImage->get(); - materialResources.colorSampler = _defaultSamplerLinear; + materialResources.colorSampler = _defaultSamplerLinear.sampler; materialResources.metalRoughImage = _whiteImage->get(); - materialResources.metalRoughSampler = _defaultSamplerLinear; + materialResources.metalRoughSampler = _defaultSamplerLinear.sampler; // set the uniform buffer for the material data const AllocatedBuffer materialConstants = create_buffer( @@ -804,7 +803,7 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { getRawDevice(), _singleImageDescriptorLayout); DescriptorWriter single_image_writer; single_image_writer.write_image(0, _errorCheckerboardImage->imageView(), - _defaultSamplerNearest, + _defaultSamplerNearest.sampler, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); single_image_writer.update_set(getRawDevice(), imageSet); diff --git a/src/graphics/vulkan/vk_loader.cpp b/src/graphics/vulkan/vk_loader.cpp index 55169c22..404cf59e 100644 --- a/src/graphics/vulkan/vk_loader.cpp +++ b/src/graphics/vulkan/vk_loader.cpp @@ -319,9 +319,9 @@ std::optional> loadGltf(VulkanEngine* engine, GLTFMetallic_Roughness::MaterialResources materialResources; materialResources.colorImage = engine->_whiteImage->get(); - materialResources.colorSampler = engine->_defaultSamplerLinear; + materialResources.colorSampler = engine->_defaultSamplerLinear.sampler; materialResources.metalRoughImage = engine->_whiteImage->get(); - materialResources.metalRoughSampler = engine->_defaultSamplerLinear; + materialResources.metalRoughSampler = engine->_defaultSamplerLinear.sampler; materialResources.dataBuffer = file.materialDataBuffer.buffer; materialResources.dataBufferOffset = data_index * sizeof(GLTFMetallic_Roughness::MaterialConstants); @@ -356,9 +356,9 @@ std::optional> loadGltf(VulkanEngine* engine, GLTFMetallic_Roughness::MaterialResources resources; resources.colorImage = engine->_whiteImage->get(); - resources.colorSampler = engine->_defaultSamplerLinear; + resources.colorSampler = engine->_defaultSamplerLinear.sampler; resources.metalRoughImage = engine->_whiteImage->get(); - resources.metalRoughSampler = engine->_defaultSamplerLinear; + resources.metalRoughSampler = engine->_defaultSamplerLinear.sampler; resources.dataBuffer = file.materialDataBuffer.buffer; resources.dataBufferOffset = 0; diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 8b360bf3..f37a4f4c 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -91,13 +91,13 @@ class VulkanEngine { ~Device() {vkDestroyDevice(_device, nullptr);} }; Device _device; + VkDevice getRawDevice() const { return _device._device; } struct Allocator { VmaAllocator _allocator{nullptr}; void init(VmaAllocatorCreateInfo info) {vmaCreateAllocator(&info, &_allocator);} // move info inside ~Allocator() {vmaDestroyAllocator(_allocator);} }; Allocator _a; // todo: rename - VkDevice getRawDevice() const { return _device._device; } Pipelines pipelines; @@ -190,8 +190,23 @@ class VulkanEngine { std::unique_ptr _greyImage; std::unique_ptr _errorCheckerboardImage; - VkSampler _defaultSamplerLinear; - VkSampler _defaultSamplerNearest; + struct Sampler { + VkSampler sampler{VK_NULL_HANDLE}; + VkDevice device{VK_NULL_HANDLE}; + const VkAllocationCallbacks* allocator{VK_NULL_HANDLE}; + void create(const VkDevice& pDevice, + const VkSamplerCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + device = pDevice; + allocator = pAllocator; + vkCreateSampler(device, pCreateInfo, pAllocator, &sampler); + } + ~Sampler() { + vkDestroySampler(device, sampler, allocator); + } + }; + Sampler _defaultSamplerLinear; + Sampler _defaultSamplerNearest; VkDescriptorSetLayout _singleImageDescriptorLayout; From 6444307d2d7ac9b2de64fbbbdf7a967ea0f7e309 Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:38:41 +0300 Subject: [PATCH 09/24] fix: memory leak Add wrapper for VkDescriptorSetLayout --- src/graphics/vulkan/pipelines.cpp | 2 +- src/graphics/vulkan/vk_engine.cpp | 32 ++++++------------------- src/include/graphics/vulkan/vk_engine.h | 21 +++++++++++++--- 3 files changed, 26 insertions(+), 29 deletions(-) diff --git a/src/graphics/vulkan/pipelines.cpp b/src/graphics/vulkan/pipelines.cpp index 6af93108..4efe1909 100644 --- a/src/graphics/vulkan/pipelines.cpp +++ b/src/graphics/vulkan/pipelines.cpp @@ -50,7 +50,7 @@ VkPipelineLayout GLTFMetallic_Roughness::create_pipeline_layout( matrixRange.size = sizeof(GPUDrawPushConstants); matrixRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - VkDescriptorSetLayout layouts[] = {engine->_gpuSceneDataDescriptorLayout, + VkDescriptorSetLayout layouts[] = {engine->_gpuSceneDataDescriptorLayout.set, materialLayout}; VkPipelineLayoutCreateInfo mesh_layout_info = vkinit::pipeline_layout_create_info(); diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index 83085651..0f6d5571 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -267,31 +267,13 @@ void VulkanEngine::init_descriptors() { globalDescriptorAllocator.init(getRawDevice(), 10, sizes); // make the descriptor set layout for our compute draw - { - DescriptorLayoutBuilder builder; - builder.add_binding(0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE); - _drawImageDescriptorLayout = - builder.build(getRawDevice(), VK_SHADER_STAGE_COMPUTE_BIT); - } - - { - DescriptorLayoutBuilder builder; - builder.add_binding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); - _gpuSceneDataDescriptorLayout = - builder.build(getRawDevice(), VK_SHADER_STAGE_VERTEX_BIT | - VK_SHADER_STAGE_FRAGMENT_BIT); - } - - { - DescriptorLayoutBuilder builder; - builder.add_binding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); - _singleImageDescriptorLayout = - builder.build(getRawDevice(), VK_SHADER_STAGE_FRAGMENT_BIT); - } + _drawImageDescriptorLayout.create(getRawDevice(), VK_SHADER_STAGE_COMPUTE_BIT, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE); + _gpuSceneDataDescriptorLayout.create(getRawDevice(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); + _singleImageDescriptorLayout.create(getRawDevice(), VK_SHADER_STAGE_FRAGMENT_BIT, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); // allocate a descriptor set for our draw image _drawImageDescriptors = globalDescriptorAllocator.allocate( - getRawDevice(), _drawImageDescriptorLayout); + getRawDevice(), _drawImageDescriptorLayout.set); DescriptorWriter writer; writer.write_image(0, _drawImage->imageView(), VK_NULL_HANDLE, @@ -317,7 +299,7 @@ void VulkanEngine::init_descriptors() { } void VulkanEngine::init_pipelines() { - pipelines.init(getRawDevice(), _singleImageDescriptorLayout, _drawImageDescriptorLayout, _drawImage->get()); + pipelines.init(getRawDevice(), _singleImageDescriptorLayout.set, _drawImageDescriptorLayout.set, _drawImage->get()); // Pipeline cleanup is handled automatically by the Pipelines object metalRoughMaterial.build_pipelines(this); } @@ -763,7 +745,7 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { // create a descriptor set that binds that buffer and update it VkDescriptorSet globalDescriptor = get_current_frame()._frameDescriptors.allocate( - getRawDevice(), _gpuSceneDataDescriptorLayout); + getRawDevice(), _gpuSceneDataDescriptorLayout.set); DescriptorWriter writer; writer.write_buffer(0, gpuSceneDataBuffer.buffer, sizeof(GPUSceneData), 0, @@ -800,7 +782,7 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { // bind a texture VkDescriptorSet imageSet = get_current_frame()._frameDescriptors.allocate( - getRawDevice(), _singleImageDescriptorLayout); + getRawDevice(), _singleImageDescriptorLayout.set); DescriptorWriter single_image_writer; single_image_writer.write_image(0, _errorCheckerboardImage->imageView(), _defaultSamplerNearest.sampler, diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index f37a4f4c..0476527c 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -161,7 +161,22 @@ class VulkanEngine { DescriptorAllocatorGrowable globalDescriptorAllocator; VkDescriptorSet _drawImageDescriptors; - VkDescriptorSetLayout _drawImageDescriptorLayout; + struct DescriptorSetLayout { + VkDescriptorSetLayout set{VK_NULL_HANDLE}; + VkDevice device{VK_NULL_HANDLE}; + void create(const VkDevice pDevice, + const VkShaderStageFlags pShaderStages, + const VkDescriptorType pType) { + device = pDevice; + DescriptorLayoutBuilder builder; + builder.add_binding(0, pType); + set = builder.build(pDevice, pShaderStages); + } + ~DescriptorSetLayout() { + vkDestroyDescriptorSetLayout(device, set, nullptr); + } + }; + DescriptorSetLayout _drawImageDescriptorLayout; GPUMeshBuffers rectangle; @@ -175,7 +190,7 @@ class VulkanEngine { GPUSceneData sceneData; - VkDescriptorSetLayout _gpuSceneDataDescriptorLayout; + DescriptorSetLayout _gpuSceneDataDescriptorLayout; AllocatedImage create_image(VkExtent3D size, VkFormat format, VkImageUsageFlags usage, @@ -208,7 +223,7 @@ class VulkanEngine { Sampler _defaultSamplerLinear; Sampler _defaultSamplerNearest; - VkDescriptorSetLayout _singleImageDescriptorLayout; + DescriptorSetLayout _singleImageDescriptorLayout; MaterialInstance defaultData; GLTFMetallic_Roughness metalRoughMaterial; From 34feffe98dd8839849d15e95f5d76c648c657911 Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:40:03 +0300 Subject: [PATCH 10/24] fix: init device for transparentPipeline --- src/graphics/vulkan/pipelines.cpp | 1 + src/graphics/vulkan/vk_engine.cpp | 64 ++++++++++--------------- src/include/graphics/vulkan/vk_engine.h | 33 ++++++------- 3 files changed, 42 insertions(+), 56 deletions(-) diff --git a/src/graphics/vulkan/pipelines.cpp b/src/graphics/vulkan/pipelines.cpp index 4efe1909..a19c35b0 100644 --- a/src/graphics/vulkan/pipelines.cpp +++ b/src/graphics/vulkan/pipelines.cpp @@ -97,6 +97,7 @@ void GLTFMetallic_Roughness::build_transparent_pipeline( transparentPipeline.pipeline = pipelineBuilder.build_pipeline(engine->getRawDevice()); + transparentPipeline._device = engine->getRawDevice(); } MaterialInstance GLTFMetallic_Roughness::write_material( diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index 0f6d5571..2d8040b7 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -50,6 +50,21 @@ constexpr bool bUseValidationLayers = false; constexpr bool bUseValidationLayers = true; #endif +void VulkanEngine::Instance::init() { + vkb::InstanceBuilder builder; + auto inst_ret = builder.set_app_name("TODO: PUT APP NAME HERE") + .set_engine_name("rainsystem") + .request_validation_layers(bUseValidationLayers) + .set_debug_callback(debugCallback) + .require_api_version(1, 3, 0) + .build(); + if (!inst_ret) { + LOGE("Failed to create Vulkan instance. Error: {}", + inst_ret.error().message()); + } + instance = inst_ret.value(); +} + VKAPI_ATTR VkBool32 VKAPI_CALL VulkanEngine::debugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, @@ -316,7 +331,7 @@ VulkanEngine::VulkanEngine(Camera& camera) : mainCamera(&camera) { command_buffers_container.init_sync_structures(this); init_descriptors(); init_pipelines(); - _imgui.init(getRawDevice(), _window.ptr, _instance._instance, _chosenGPU, _graphicsQueue, &_swapchainImageFormat); + _imgui.init(getRawDevice(), _window.ptr, _instance.instance, _chosenGPU, _graphicsQueue, &_swapchainImageFormat); init_default_data(); mainCamera->velocity = glm::vec3(0.f); @@ -355,27 +370,10 @@ void VulkanEngine::init_vulkan() { for (auto& [extensionName, _] : system_info.available_extensions) { LOGI(extensionName); } - - vkb::InstanceBuilder builder; - - auto inst_ret = builder.set_app_name("TODO: PUT APP NAME HERE") - .set_engine_name("rainsystem") - .request_validation_layers(bUseValidationLayers) - .set_debug_callback(debugCallback) - .require_api_version(1, 3, 0) - .build(); - - if (!inst_ret) { - LOGE("Failed to create Vulkan instance. Error: {}", - inst_ret.error().message()); - } - - vkb::Instance vkb_inst = inst_ret.value(); - // grab the instance - _instance.init(vkb_inst); + _instance.init(); - SDL_bool err = SDL_Vulkan_CreateSurface(_window.ptr, _instance._instance, &_surface); + SDL_bool err = SDL_Vulkan_CreateSurface(_window.ptr, _instance.instance, &_surface); if (!err) { LOGE("Failed to create Vulkan surface. Error: {}", SDL_GetError()); } @@ -393,7 +391,7 @@ void VulkanEngine::init_vulkan() { // use vkbootstrap to select a gpu. // We want a gpu that can write to the SDL surface and supports vulkan 1.3 // with the correct features - vkb::PhysicalDeviceSelector selector{vkb_inst}; + vkb::PhysicalDeviceSelector selector{_instance.instance}; auto physical_device_ret = selector.set_minimum_version(1, 3) .set_required_features_13(features) @@ -406,23 +404,13 @@ void VulkanEngine::init_vulkan() { physical_device_ret.error().message()); } - const vkb::PhysicalDevice& physicalDevice = physical_device_ret.value(); - - vkb::DeviceBuilder deviceBuilder{physicalDevice}; - - auto dev_ret = deviceBuilder.build(); - if (!dev_ret) { - LOGE("Failed to create logical device. Error: {}", - dev_ret.error().message()); - } - - const vkb::Device& vkbDevice = dev_ret.value(); + const vkb::PhysicalDevice& physical_device = physical_device_ret.value(); // Get the VkDevice handle used in the rest of a vulkan application - _device.init(vkbDevice); - _chosenGPU = physicalDevice.physical_device; + _device.init(physical_device); + _chosenGPU = physical_device.physical_device; - auto queue_ret = vkbDevice.get_queue(vkb::QueueType::graphics); + auto queue_ret = _device.device.get_queue(vkb::QueueType::graphics); if (!queue_ret) { LOGE("Failed to retrieve graphics queue. Error: {}", queue_ret.error().message()); @@ -430,7 +418,7 @@ void VulkanEngine::init_vulkan() { _graphicsQueue = queue_ret.value(); - auto queue_family_ret = vkbDevice.get_queue_index(vkb::QueueType::graphics); + auto queue_family_ret = _device.device.get_queue_index(vkb::QueueType::graphics); if (!queue_family_ret) { LOGE("Failed to retrieve graphics queue family. Error: {}", queue_family_ret.error().message()); @@ -442,7 +430,7 @@ void VulkanEngine::init_vulkan() { VmaAllocatorCreateInfo allocatorInfo = {}; allocatorInfo.physicalDevice = _chosenGPU; allocatorInfo.device = getRawDevice(); - allocatorInfo.instance = _instance._instance; + allocatorInfo.instance = _instance.instance; allocatorInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; // // vmaCreateAllocator(&allocatorInfo, &_a._allocator); _a.init(allocatorInfo); @@ -597,7 +585,7 @@ void VulkanEngine::destroy_swapchain() { destroy_swapchain(); - vkDestroySurfaceKHR(_instance._instance, _surface, nullptr); + vkDestroySurfaceKHR(_instance.instance, _surface, nullptr); } // clear engine pointer diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 0476527c..3793bbbb 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -65,33 +65,30 @@ struct DrawContext { class VulkanEngine { public: - - struct Instance { - VkInstance _instance{nullptr}; // Vulkan library handle //todo: get - VkDebugUtilsMessengerEXT _debug_messenger{nullptr}; // Vulkan debug output handle - void init(const vkb::Instance& vkb_inst) { - _instance = vkb_inst.instance; - _debug_messenger = vkb_inst.debug_messenger; - } + vkb::Instance instance; + void init(); ~Instance() { - if (_debug_messenger) { - vkb::destroy_debug_utils_messenger(_instance, _debug_messenger); - } - if (_instance) { - vkDestroyInstance(_instance, nullptr); - } + vkb::destroy_instance(instance); } }; Instance _instance; struct Device { - VkDevice _device{nullptr}; // Vulkan device for commands - void init(const vkb::Device& dev) { _device = dev.device; } - ~Device() {vkDestroyDevice(_device, nullptr);} + vkb::Device device; + void init(const vkb::PhysicalDevice& physical_device) { + vkb::DeviceBuilder device_builder{physical_device}; + auto dev_ret = device_builder.build(); + if (!dev_ret) { + LOGE("Failed to create logical device. Error: {}", + dev_ret.error().message()); + } + device = dev_ret.value(); + } + ~Device() {vkb::destroy_device(device);} }; Device _device; - VkDevice getRawDevice() const { return _device._device; } + VkDevice getRawDevice() const { return _device.device; } struct Allocator { VmaAllocator _allocator{nullptr}; void init(VmaAllocatorCreateInfo info) {vmaCreateAllocator(&info, &_allocator);} // move info inside From b49bf5db1730ab62bbfba4e0469753057a23863d Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:41:06 +0300 Subject: [PATCH 11/24] fix: memory leak Add dtor for GLTFMetallic_Roughness --- src/graphics/vulkan/pipelines.cpp | 6 ++---- src/graphics/vulkan/vk_engine.cpp | 2 +- src/graphics/vulkan/vk_loader.cpp | 4 ++-- src/include/graphics/vulkan/pipelines.h | 9 +++++++-- src/include/graphics/vulkan/vk_engine.h | 2 +- src/include/graphics/vulkan/vk_types.h | 9 --------- 6 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/graphics/vulkan/pipelines.cpp b/src/graphics/vulkan/pipelines.cpp index a19c35b0..a40811d9 100644 --- a/src/graphics/vulkan/pipelines.cpp +++ b/src/graphics/vulkan/pipelines.cpp @@ -9,7 +9,7 @@ void GLTFMetallic_Roughness::build_pipelines(VulkanEngine* engine) { create_material_layout(engine); VkPipelineLayout newLayout = create_pipeline_layout(engine); - + device = engine->getRawDevice(); opaquePipeline.layout = newLayout; transparentPipeline.layout = newLayout; @@ -83,7 +83,6 @@ void GLTFMetallic_Roughness::build_opaque_pipeline(VulkanEngine* engine, pipelineBuilder._pipelineLayout = layout; opaquePipeline.pipeline = pipelineBuilder.build_pipeline(engine->getRawDevice()); - opaquePipeline._device = engine->getRawDevice(); } void GLTFMetallic_Roughness::build_transparent_pipeline( @@ -97,11 +96,10 @@ void GLTFMetallic_Roughness::build_transparent_pipeline( transparentPipeline.pipeline = pipelineBuilder.build_pipeline(engine->getRawDevice()); - transparentPipeline._device = engine->getRawDevice(); } MaterialInstance GLTFMetallic_Roughness::write_material( - VkDevice device, MaterialPass pass, const MaterialResources& resources, + MaterialPass pass, const MaterialResources& resources, DescriptorAllocatorGrowable& descriptorAllocator) { MaterialInstance matData{}; matData.passType = pass; diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index 2d8040b7..0c05618b 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -206,7 +206,7 @@ void VulkanEngine::init_default_data() { materialResources.dataBufferOffset = 0; defaultData = metalRoughMaterial.write_material( - getRawDevice(), MaterialPass::MainColor, materialResources, + MaterialPass::MainColor, materialResources, globalDescriptorAllocator); } diff --git a/src/graphics/vulkan/vk_loader.cpp b/src/graphics/vulkan/vk_loader.cpp index 404cf59e..86c1e75e 100644 --- a/src/graphics/vulkan/vk_loader.cpp +++ b/src/graphics/vulkan/vk_loader.cpp @@ -338,7 +338,7 @@ std::optional> loadGltf(VulkanEngine* engine, } newMat->data = engine->metalRoughMaterial.write_material( - engine->getRawDevice(), passType, materialResources, + passType, materialResources, file.descriptorPool); data_index++; } @@ -363,7 +363,7 @@ std::optional> loadGltf(VulkanEngine* engine, resources.dataBufferOffset = 0; defaultMat->data = engine->metalRoughMaterial.write_material( - engine->getRawDevice(), MaterialPass::MainColor, resources, + MaterialPass::MainColor, resources, file.descriptorPool); } diff --git a/src/include/graphics/vulkan/pipelines.h b/src/include/graphics/vulkan/pipelines.h index 91be1a80..1e377557 100644 --- a/src/include/graphics/vulkan/pipelines.h +++ b/src/include/graphics/vulkan/pipelines.h @@ -37,9 +37,14 @@ struct GLTFMetallic_Roughness { DescriptorWriter writer; void build_pipelines(VulkanEngine* engine); - void clear_resources(VkDevice device); + VkDevice device{VK_NULL_HANDLE}; + ~GLTFMetallic_Roughness() { + vkDestroyPipelineLayout(device, opaquePipeline.layout, nullptr); + vkDestroyPipeline(device, opaquePipeline.pipeline, nullptr); + vkDestroyPipeline(device, transparentPipeline.pipeline, nullptr); + } MaterialInstance write_material( - VkDevice device, MaterialPass pass, + MaterialPass pass, const MaterialResources& resources, DescriptorAllocatorGrowable& descriptorAllocator); diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 3793bbbb..9ef824f7 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -77,7 +77,7 @@ class VulkanEngine { struct Device { vkb::Device device; void init(const vkb::PhysicalDevice& physical_device) { - vkb::DeviceBuilder device_builder{physical_device}; + const vkb::DeviceBuilder device_builder{physical_device}; auto dev_ret = device_builder.build(); if (!dev_ret) { LOGE("Failed to create logical device. Error: {}", diff --git a/src/include/graphics/vulkan/vk_types.h b/src/include/graphics/vulkan/vk_types.h index fd2b490a..172f637e 100644 --- a/src/include/graphics/vulkan/vk_types.h +++ b/src/include/graphics/vulkan/vk_types.h @@ -65,15 +65,6 @@ enum class MaterialPass : uint8_t { MainColor, Transparent, Other }; struct MaterialPipeline { VkPipeline pipeline; VkPipelineLayout layout; - VkDevice _device = VK_NULL_HANDLE; - ~MaterialPipeline() { - if (_device != VK_NULL_HANDLE) { - vkDestroyPipelineLayout(_device, layout, nullptr); - vkDestroyPipeline(_device, pipeline, nullptr); - pipeline = VK_NULL_HANDLE; - layout = VK_NULL_HANDLE; - } - } }; struct MaterialInstance { From 7c7aaab2b34579bce1f5e3767d6acae1a967fbfc Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:42:54 +0300 Subject: [PATCH 12/24] fix: memory leak By using DescriptorSetLayout outside VulkanEngine for materialLayout --- src/graphics/CMakeLists.txt | 1 + src/graphics/vulkan/DescriptorSetLayout.cpp | 18 ++++++++++++++++++ src/graphics/vulkan/pipelines.cpp | 17 +++++++---------- src/graphics/vulkan/vk_engine.cpp | 7 ++++--- .../graphics/vulkan/DescriptorSetLayout.h | 14 ++++++++++++++ src/include/graphics/vulkan/pipelines.h | 11 ++++------- src/include/graphics/vulkan/vk_engine.h | 17 ++--------------- 7 files changed, 50 insertions(+), 35 deletions(-) create mode 100644 src/graphics/vulkan/DescriptorSetLayout.cpp create mode 100644 src/include/graphics/vulkan/DescriptorSetLayout.h diff --git a/src/graphics/CMakeLists.txt b/src/graphics/CMakeLists.txt index e117fd76..b1c1e7f3 100644 --- a/src/graphics/CMakeLists.txt +++ b/src/graphics/CMakeLists.txt @@ -10,6 +10,7 @@ target_sources(${PROJECT_NAME} vulkan/vk_pipelines.cpp vulkan/pipelines.cpp vulkan/ComputePipeline.cpp + vulkan/DescriptorSetLayout.cpp vulkan/GraphicsPipeline.cpp Graphics.cpp ) \ No newline at end of file diff --git a/src/graphics/vulkan/DescriptorSetLayout.cpp b/src/graphics/vulkan/DescriptorSetLayout.cpp new file mode 100644 index 00000000..262b8234 --- /dev/null +++ b/src/graphics/vulkan/DescriptorSetLayout.cpp @@ -0,0 +1,18 @@ +#include "graphics/vulkan/DescriptorSetLayout.h" + +#include "graphics/vulkan/vk_descriptors.h" + +void DescriptorSetLayout::create( + const VkDevice pDevice, const VkShaderStageFlags pShaderStages, + std::initializer_list> bindings) { + device = pDevice; + DescriptorLayoutBuilder builder; + for (const auto& [binding, type] : bindings) { + builder.add_binding(binding, type); + } + set = builder.build(pDevice, pShaderStages); +} + +DescriptorSetLayout::~DescriptorSetLayout() { + vkDestroyDescriptorSetLayout(device, set, nullptr); +} diff --git a/src/graphics/vulkan/pipelines.cpp b/src/graphics/vulkan/pipelines.cpp index a40811d9..43bb90f6 100644 --- a/src/graphics/vulkan/pipelines.cpp +++ b/src/graphics/vulkan/pipelines.cpp @@ -33,14 +33,11 @@ VkShaderModule GLTFMetallic_Roughness::load_shader(VulkanEngine* engine, } void GLTFMetallic_Roughness::create_material_layout(VulkanEngine* engine) { - DescriptorLayoutBuilder layoutBuilder; - layoutBuilder.add_binding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); - layoutBuilder.add_binding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); - layoutBuilder.add_binding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); - - materialLayout = layoutBuilder.build( - engine->getRawDevice(), - VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT); + materialLayout.create(engine->getRawDevice(), + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + {{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER}, + {1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}, + {2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}}); } VkPipelineLayout GLTFMetallic_Roughness::create_pipeline_layout( @@ -51,7 +48,7 @@ VkPipelineLayout GLTFMetallic_Roughness::create_pipeline_layout( matrixRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; VkDescriptorSetLayout layouts[] = {engine->_gpuSceneDataDescriptorLayout.set, - materialLayout}; + materialLayout.set}; VkPipelineLayoutCreateInfo mesh_layout_info = vkinit::pipeline_layout_create_info(); mesh_layout_info.setLayoutCount = 2; @@ -106,7 +103,7 @@ MaterialInstance GLTFMetallic_Roughness::write_material( matData.pipeline = (pass == MaterialPass::Transparent) ? &transparentPipeline : &opaquePipeline; - matData.materialSet = descriptorAllocator.allocate(device, materialLayout); + matData.materialSet = descriptorAllocator.allocate(device, materialLayout.set); writer.clear(); writer.write_buffer(0, resources.dataBuffer, sizeof(MaterialConstants), diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index 0c05618b..58413b8a 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -17,6 +17,7 @@ #include "core/Logging.h" #include "core/config.h" #include "graphics/vulkan/vk_descriptors.h" +#include "graphics/vulkan/DescriptorSetLayout.h" #include "scene/Camera.h" @@ -282,9 +283,9 @@ void VulkanEngine::init_descriptors() { globalDescriptorAllocator.init(getRawDevice(), 10, sizes); // make the descriptor set layout for our compute draw - _drawImageDescriptorLayout.create(getRawDevice(), VK_SHADER_STAGE_COMPUTE_BIT, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE); - _gpuSceneDataDescriptorLayout.create(getRawDevice(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); - _singleImageDescriptorLayout.create(getRawDevice(), VK_SHADER_STAGE_FRAGMENT_BIT, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); + _drawImageDescriptorLayout.create(getRawDevice(), VK_SHADER_STAGE_COMPUTE_BIT, {{0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE}}); + _gpuSceneDataDescriptorLayout.create(getRawDevice(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, {{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER}}); + _singleImageDescriptorLayout.create(getRawDevice(), VK_SHADER_STAGE_FRAGMENT_BIT, {{0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}}); // allocate a descriptor set for our draw image _drawImageDescriptors = globalDescriptorAllocator.allocate( diff --git a/src/include/graphics/vulkan/DescriptorSetLayout.h b/src/include/graphics/vulkan/DescriptorSetLayout.h new file mode 100644 index 00000000..44d2b309 --- /dev/null +++ b/src/include/graphics/vulkan/DescriptorSetLayout.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include +#include + +struct DescriptorSetLayout { + VkDescriptorSetLayout set{VK_NULL_HANDLE}; + VkDevice device{VK_NULL_HANDLE}; + void create(VkDevice pDevice, VkShaderStageFlags pShaderStages, + std::initializer_list> + bindings); + + ~DescriptorSetLayout(); +}; \ No newline at end of file diff --git a/src/include/graphics/vulkan/pipelines.h b/src/include/graphics/vulkan/pipelines.h index 1e377557..14a933a6 100644 --- a/src/include/graphics/vulkan/pipelines.h +++ b/src/include/graphics/vulkan/pipelines.h @@ -2,14 +2,11 @@ #include +#include "ComputePipeline.h" +#include "DescriptorSetLayout.h" +#include "GraphicsPipeline.h" #include "vk_descriptors.h" -#include "vk_images.h" -#include "vk_initializers.h" -#include "vk_pipelines.h" #include "vk_types.h" -#include "IPipeline.h" -#include "GraphicsPipeline.h" -#include "ComputePipeline.h" class VulkanEngine; @@ -17,7 +14,7 @@ struct GLTFMetallic_Roughness { MaterialPipeline opaquePipeline; MaterialPipeline transparentPipeline; - VkDescriptorSetLayout materialLayout; + DescriptorSetLayout materialLayout; struct MaterialConstants { glm::vec4 colorFactors; diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 9ef824f7..1d8c788c 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -17,6 +17,7 @@ #include #include "ComputePipeline.h" +#include "DescriptorSetLayout.h" #include "core/ModelImpl.h" #include "pipelines.h" #include "vk_command_buffers.h" @@ -158,21 +159,7 @@ class VulkanEngine { DescriptorAllocatorGrowable globalDescriptorAllocator; VkDescriptorSet _drawImageDescriptors; - struct DescriptorSetLayout { - VkDescriptorSetLayout set{VK_NULL_HANDLE}; - VkDevice device{VK_NULL_HANDLE}; - void create(const VkDevice pDevice, - const VkShaderStageFlags pShaderStages, - const VkDescriptorType pType) { - device = pDevice; - DescriptorLayoutBuilder builder; - builder.add_binding(0, pType); - set = builder.build(pDevice, pShaderStages); - } - ~DescriptorSetLayout() { - vkDestroyDescriptorSetLayout(device, set, nullptr); - } - }; + DescriptorSetLayout _drawImageDescriptorLayout; From 8ebaa3058d354f9ce952cc7cd72eee8693274af1 Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:44:08 +0300 Subject: [PATCH 13/24] fix: double free --- src/graphics/vulkan/vk_engine.cpp | 6 ------ src/include/graphics/vulkan/vk_smart_wrappers.h | 6 +++--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index 58413b8a..fd23ea02 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -573,12 +573,6 @@ void VulkanEngine::destroy_swapchain() { // Smart pointers will automatically clean up resources for (auto& _frame : command_buffers_container._frames) { - // Smart pointers automatically clean up sync objects - // Manual cleanup only for command pools and command buffers - if (_frame._commandPool) { - vkDestroyCommandPool(getRawDevice(), _frame._commandPool->get(), nullptr); - } - // Destroy frame descriptors manually _frame._frameDescriptors.destroy_pools(getRawDevice()); } diff --git a/src/include/graphics/vulkan/vk_smart_wrappers.h b/src/include/graphics/vulkan/vk_smart_wrappers.h index 46bad26c..568fddf3 100644 --- a/src/include/graphics/vulkan/vk_smart_wrappers.h +++ b/src/include/graphics/vulkan/vk_smart_wrappers.h @@ -97,9 +97,9 @@ class VulkanCommandPool { VulkanCommandPool(VkDevice device, VkCommandPool commandPool) : device_(device), commandPool_(commandPool) {} ~VulkanCommandPool() { - // if (commandPool_ != VK_NULL_HANDLE) { - // vkDestroyCommandPool(device_, commandPool_, nullptr); - // } + if (commandPool_ != VK_NULL_HANDLE) { + vkDestroyCommandPool(device_, commandPool_, nullptr); + } } // Move constructor and assignment From a290867d752e5f885bac21e07863e198d75d7ec4 Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:45:17 +0300 Subject: [PATCH 14/24] fix: imgui destruction --- src/graphics/vulkan/vk_engine.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index fd23ea02..e7f57042 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -268,10 +268,10 @@ void VulkanEngine::init_default_data() { ImGui_ImplVulkan_CreateFontsTexture(); } -#include + VulkanEngine::Imgui::~Imgui() { - vkDestroyDescriptorPool(_device, _imguiPool, nullptr); ImGui_ImplVulkan_Shutdown(); + vkDestroyDescriptorPool(_device, _imguiPool, nullptr); } void VulkanEngine::init_descriptors() { From 042653447cfeeabc55cd8a32b94afc84eb1f43eb Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:46:48 +0300 Subject: [PATCH 15/24] chore: delete useless createInstance --- demo/main.cpp | 3 ++- src/core/CMakeLists.txt | 1 - src/core/createInstance.cpp | 9 --------- 3 files changed, 2 insertions(+), 11 deletions(-) delete mode 100644 src/core/createInstance.cpp diff --git a/demo/main.cpp b/demo/main.cpp index 7815afde..5ebc9c0a 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -3,10 +3,11 @@ #include #include "IController.h" +#include "core/Controller.h" int main([[maybe_unused]] int argc, [[maybe_unused]] char* args[]) { try { - const auto controller = createInstance(); + const auto controller = createController(); controller->run(); } catch (const std::runtime_error& e) { std::cerr << "Unhandled exception: " << e.what() << '\n'; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 987b4319..9a3b1a9b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -7,5 +7,4 @@ target_sources(${PROJECT_NAME} ModelImpl.cpp View.cpp ViewImpl.cpp - createInstance.cpp ) \ No newline at end of file diff --git a/src/core/createInstance.cpp b/src/core/createInstance.cpp deleted file mode 100644 index eb69fadf..00000000 --- a/src/core/createInstance.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "IController.h" -#include "core/Controller.h" -#include "core/Model.h" - -IController::Ptr createInstance() { - - const auto controller = createController(); - return controller; -} From f653bd5d4caae5c3363cdac061e09323f0a28a00 Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:47:57 +0300 Subject: [PATCH 16/24] fix: valgrind uninitialised value --- src/include/graphics/vulkan/vk_engine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 1d8c788c..228d7fe3 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -170,7 +170,7 @@ class VulkanEngine { std::vector> testMeshes; - bool resize_requested; + bool resize_requested{true}; GPUSceneData sceneData; From c71ccfcd81f142bc84f593f02a84ef220cdde37d Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:50:08 +0300 Subject: [PATCH 17/24] refactor: move dtor .cpp --- src/graphics/vulkan/vk_engine.cpp | 4 ++++ src/include/graphics/vulkan/vk_engine.h | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index e7f57042..df35b40f 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -66,6 +66,10 @@ void VulkanEngine::Instance::init() { instance = inst_ret.value(); } +VulkanEngine::Instance::~Instance() { + vkb::destroy_instance(instance); +} + VKAPI_ATTR VkBool32 VKAPI_CALL VulkanEngine::debugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 228d7fe3..9e9a4cda 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -69,9 +69,7 @@ class VulkanEngine { struct Instance { vkb::Instance instance; void init(); - ~Instance() { - vkb::destroy_instance(instance); - } + ~Instance(); }; Instance _instance; From a6b3306245aad8c97d8df09bf12701e46495ce8d Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:51:03 +0300 Subject: [PATCH 18/24] refactor: Instance --- src/graphics/vulkan/vk_engine.cpp | 24 +++++++++++++----------- src/include/graphics/vulkan/vk_engine.h | 9 ++++++--- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index df35b40f..6d3d26a5 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -37,7 +37,6 @@ #include "graphics/vulkan/vk_loader.h" #include "graphics/vulkan/vk_pipelines.h" #include "graphics/vulkan/vk_types.h" -#include "graphics/vulkan/vk_command_buffers.h" VulkanEngine* loadedEngine = nullptr; @@ -63,13 +62,16 @@ void VulkanEngine::Instance::init() { LOGE("Failed to create Vulkan instance. Error: {}", inst_ret.error().message()); } - instance = inst_ret.value(); + _instance = inst_ret.value(); } VulkanEngine::Instance::~Instance() { - vkb::destroy_instance(instance); + vkb::destroy_instance(_instance); } +VulkanEngine::Instance::operator VkInstance() const { return _instance; } +VulkanEngine::Instance::operator vkb::Instance() const { return _instance; } + VKAPI_ATTR VkBool32 VKAPI_CALL VulkanEngine::debugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, @@ -240,7 +242,7 @@ void VulkanEngine::init_default_data() { } void VulkanEngine::Imgui::init(const VkDevice& dev, SDL_Window* w, - const VkInstance& instance, const VkPhysicalDevice& physicalDevice, + const VkInstance& pInstance, const VkPhysicalDevice& physicalDevice, const VkQueue& queue, const VkFormat* format) { _device = dev; initImguiPool(); @@ -250,7 +252,7 @@ void VulkanEngine::init_default_data() { // this initializes imgui for SDL ImGui_ImplSDL2_InitForVulkan(w); // this initializes imgui for Vulkan - ImGui_ImplVulkan_InitInfo init_info = {.Instance = instance, + ImGui_ImplVulkan_InitInfo init_info = {.Instance = pInstance, .PhysicalDevice = physicalDevice, .Device = _device, .Queue = queue, @@ -336,7 +338,7 @@ VulkanEngine::VulkanEngine(Camera& camera) : mainCamera(&camera) { command_buffers_container.init_sync_structures(this); init_descriptors(); init_pipelines(); - _imgui.init(getRawDevice(), _window.ptr, _instance.instance, _chosenGPU, _graphicsQueue, &_swapchainImageFormat); + _imgui.init(getRawDevice(), _window.ptr, static_cast(instance), _chosenGPU, _graphicsQueue, &_swapchainImageFormat); init_default_data(); mainCamera->velocity = glm::vec3(0.f); @@ -376,9 +378,9 @@ void VulkanEngine::init_vulkan() { LOGI(extensionName); } // grab the instance - _instance.init(); + instance.init(); - SDL_bool err = SDL_Vulkan_CreateSurface(_window.ptr, _instance.instance, &_surface); + SDL_bool err = SDL_Vulkan_CreateSurface(_window.ptr, static_cast(instance), &_surface); if (!err) { LOGE("Failed to create Vulkan surface. Error: {}", SDL_GetError()); } @@ -396,7 +398,7 @@ void VulkanEngine::init_vulkan() { // use vkbootstrap to select a gpu. // We want a gpu that can write to the SDL surface and supports vulkan 1.3 // with the correct features - vkb::PhysicalDeviceSelector selector{_instance.instance}; + vkb::PhysicalDeviceSelector selector{static_cast(instance)}; auto physical_device_ret = selector.set_minimum_version(1, 3) .set_required_features_13(features) @@ -435,7 +437,7 @@ void VulkanEngine::init_vulkan() { VmaAllocatorCreateInfo allocatorInfo = {}; allocatorInfo.physicalDevice = _chosenGPU; allocatorInfo.device = getRawDevice(); - allocatorInfo.instance = _instance.instance; + allocatorInfo.instance = static_cast(instance); allocatorInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; // // vmaCreateAllocator(&allocatorInfo, &_a._allocator); _a.init(allocatorInfo); @@ -584,7 +586,7 @@ void VulkanEngine::destroy_swapchain() { destroy_swapchain(); - vkDestroySurfaceKHR(_instance.instance, _surface, nullptr); + vkDestroySurfaceKHR(static_cast(instance), _surface, nullptr); } // clear engine pointer diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 9e9a4cda..861a318f 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -67,11 +67,14 @@ struct DrawContext { class VulkanEngine { public: struct Instance { - vkb::Instance instance; + explicit operator VkInstance() const; + explicit operator vkb::Instance() const; void init(); ~Instance(); + private: + vkb::Instance _instance; }; - Instance _instance; + Instance instance; struct Device { vkb::Device device; @@ -227,7 +230,7 @@ class VulkanEngine { private: struct Imgui { void init(const VkDevice& dev, SDL_Window* w, - const VkInstance& instance, const VkPhysicalDevice& physicalDevice, + const VkInstance& pInstance, const VkPhysicalDevice& physicalDevice, const VkQueue& queue, const VkFormat* format); ~Imgui(); private: From 3a66a1a6d13955a51db3fac960e945bc68db5990 Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:52:50 +0300 Subject: [PATCH 19/24] refactor: Device --- src/graphics/vulkan/pipelines.cpp | 16 +-- src/graphics/vulkan/vk_command_buffers.cpp | 16 +-- .../vulkan/vk_command_buffers_container.cpp | 16 +-- src/graphics/vulkan/vk_engine.cpp | 97 +++++++++++-------- src/graphics/vulkan/vk_loader.cpp | 6 +- src/include/graphics/vulkan/vk_engine.h | 21 ++-- 6 files changed, 91 insertions(+), 81 deletions(-) diff --git a/src/graphics/vulkan/pipelines.cpp b/src/graphics/vulkan/pipelines.cpp index 43bb90f6..0e2c66b2 100644 --- a/src/graphics/vulkan/pipelines.cpp +++ b/src/graphics/vulkan/pipelines.cpp @@ -9,7 +9,7 @@ void GLTFMetallic_Roughness::build_pipelines(VulkanEngine* engine) { create_material_layout(engine); VkPipelineLayout newLayout = create_pipeline_layout(engine); - device = engine->getRawDevice(); + device = static_cast(engine->device); opaquePipeline.layout = newLayout; transparentPipeline.layout = newLayout; @@ -17,15 +17,15 @@ void GLTFMetallic_Roughness::build_pipelines(VulkanEngine* engine) { build_transparent_pipeline(engine, meshVertexShader, meshFragShader, newLayout); - vkDestroyShaderModule(engine->getRawDevice(), meshFragShader, nullptr); - vkDestroyShaderModule(engine->getRawDevice(), meshVertexShader, nullptr); + vkDestroyShaderModule(static_cast(engine->device), meshFragShader, nullptr); + vkDestroyShaderModule(static_cast(engine->device), meshVertexShader, nullptr); } VkShaderModule GLTFMetallic_Roughness::load_shader(VulkanEngine* engine, const char* relative_path, const char* type) { VkShaderModule shaderModule; - if (!vkutil::load_shader_module(relative_path, engine->getRawDevice(), + if (!vkutil::load_shader_module(relative_path, static_cast(engine->device), &shaderModule)) { fmt::println("Error when building the {} shader module", type); } @@ -33,7 +33,7 @@ VkShaderModule GLTFMetallic_Roughness::load_shader(VulkanEngine* engine, } void GLTFMetallic_Roughness::create_material_layout(VulkanEngine* engine) { - materialLayout.create(engine->getRawDevice(), + materialLayout.create(static_cast(engine->device), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, {{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER}, {1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}, @@ -57,7 +57,7 @@ VkPipelineLayout GLTFMetallic_Roughness::create_pipeline_layout( mesh_layout_info.pushConstantRangeCount = 1; VkPipelineLayout newLayout; - VK_CHECK(vkCreatePipelineLayout(engine->getRawDevice(), &mesh_layout_info, nullptr, + VK_CHECK(vkCreatePipelineLayout(static_cast(engine->device), &mesh_layout_info, nullptr, &newLayout)); return newLayout; @@ -79,7 +79,7 @@ void GLTFMetallic_Roughness::build_opaque_pipeline(VulkanEngine* engine, pipelineBuilder.set_depth_format(engine->_depthImage->get().imageFormat); pipelineBuilder._pipelineLayout = layout; - opaquePipeline.pipeline = pipelineBuilder.build_pipeline(engine->getRawDevice()); + opaquePipeline.pipeline = pipelineBuilder.build_pipeline(static_cast(engine->device)); } void GLTFMetallic_Roughness::build_transparent_pipeline( @@ -92,7 +92,7 @@ void GLTFMetallic_Roughness::build_transparent_pipeline( pipelineBuilder._pipelineLayout = layout; transparentPipeline.pipeline = - pipelineBuilder.build_pipeline(engine->getRawDevice()); + pipelineBuilder.build_pipeline(static_cast(engine->device)); } MaterialInstance GLTFMetallic_Roughness::write_material( diff --git a/src/graphics/vulkan/vk_command_buffers.cpp b/src/graphics/vulkan/vk_command_buffers.cpp index 9a748dbe..cee68e8e 100644 --- a/src/graphics/vulkan/vk_command_buffers.cpp +++ b/src/graphics/vulkan/vk_command_buffers.cpp @@ -13,31 +13,31 @@ void CommandBuffers::init_commands(VulkanEngine* vk_engine) { for (auto& _frame : vk_engine->command_buffers_container._frames) { VkCommandPool commandPool; - VK_CHECK(vkCreateCommandPool(vk_engine->getRawDevice(), &commandPoolInfo, + VK_CHECK(vkCreateCommandPool(static_cast(vk_engine->device), &commandPoolInfo, nullptr, &commandPool)); - _frame._commandPool = std::make_unique(vk_engine->getRawDevice(), commandPool); + _frame._commandPool = std::make_unique(static_cast(vk_engine->device), commandPool); // allocate the default command buffer that we will use for rendering VkCommandBufferAllocateInfo cmdAllocInfo = vkinit::command_buffer_allocate_info(_frame._commandPool->get(), 1); - VK_CHECK(vkAllocateCommandBuffers(vk_engine->getRawDevice(), &cmdAllocInfo, + VK_CHECK(vkAllocateCommandBuffers(static_cast(vk_engine->device), &cmdAllocInfo, &_frame._mainCommandBuffer)); } VkCommandPool immCommandPool; - VK_CHECK(vkCreateCommandPool(vk_engine->getRawDevice(), &commandPoolInfo, nullptr, + VK_CHECK(vkCreateCommandPool(static_cast(vk_engine->device), &commandPoolInfo, nullptr, &immCommandPool)); - vk_engine->command_buffers_container._immCommandPool = std::make_unique(vk_engine->getRawDevice(), immCommandPool); + vk_engine->command_buffers_container._immCommandPool = std::make_unique(static_cast(vk_engine->device), immCommandPool); // allocate the command buffer for immediate submits const VkCommandBufferAllocateInfo cmdAllocInfo = vkinit::command_buffer_allocate_info(vk_engine->command_buffers_container._immCommandPool->get(), 1); - VK_CHECK(vkAllocateCommandBuffers(vk_engine->getRawDevice(), &cmdAllocInfo, + VK_CHECK(vkAllocateCommandBuffers(static_cast(vk_engine->device), &cmdAllocInfo, &(vk_engine->command_buffers_container._immCommandBuffer))); // Smart pointer will automatically handle cleanup - no need for deletion queue @@ -46,7 +46,7 @@ void CommandBuffers::init_commands(VulkanEngine* vk_engine) { void CommandBuffers::immediate_submit( std::function&& function, VulkanEngine* vk_engine) const { - VK_CHECK(vkResetFences(vk_engine->getRawDevice(), 1, vk_engine->command_buffers_container._immFence->getPtr())); + VK_CHECK(vkResetFences(static_cast(vk_engine->device), 1, vk_engine->command_buffers_container._immFence->getPtr())); VK_CHECK(vkResetCommandBuffer(vk_engine->command_buffers_container._immCommandBuffer, 0)); const VkCommandBuffer cmd = vk_engine->command_buffers_container._immCommandBuffer; @@ -71,6 +71,6 @@ void CommandBuffers::immediate_submit( VK_CHECK(vkQueueSubmit2(vk_engine->_graphicsQueue, 1, &submit, vk_engine->command_buffers_container._immFence->get())); - VK_CHECK(vkWaitForFences(vk_engine->getRawDevice(), 1, vk_engine->command_buffers_container._immFence->getPtr(), true, + VK_CHECK(vkWaitForFences(static_cast(vk_engine->device), 1, vk_engine->command_buffers_container._immFence->getPtr(), true, 9999999999)); } diff --git a/src/graphics/vulkan/vk_command_buffers_container.cpp b/src/graphics/vulkan/vk_command_buffers_container.cpp index 8a5b48af..85137f11 100644 --- a/src/graphics/vulkan/vk_command_buffers_container.cpp +++ b/src/graphics/vulkan/vk_command_buffers_container.cpp @@ -15,18 +15,18 @@ void CommandBuffersContainer::init_sync_structures(VulkanEngine* vk_engine) { for (auto& _frame : _frames) { VkFence renderFence; - VK_CHECK(vkCreateFence(vk_engine->getRawDevice(), &fenceCreateInfo, nullptr, &renderFence)); - _frame._renderFence = std::make_unique(vk_engine->getRawDevice(), renderFence); + VK_CHECK(vkCreateFence(static_cast(vk_engine->device), &fenceCreateInfo, nullptr, &renderFence)); + _frame._renderFence = std::make_unique(static_cast(vk_engine->device), renderFence); VkSemaphore swapchainSemaphore, renderSemaphore; - VK_CHECK(vkCreateSemaphore(vk_engine->getRawDevice(), &semaphoreCreateInfo, nullptr, &swapchainSemaphore)); - VK_CHECK(vkCreateSemaphore(vk_engine->getRawDevice(), &semaphoreCreateInfo, nullptr, &renderSemaphore)); + VK_CHECK(vkCreateSemaphore(static_cast(vk_engine->device), &semaphoreCreateInfo, nullptr, &swapchainSemaphore)); + VK_CHECK(vkCreateSemaphore(static_cast(vk_engine->device), &semaphoreCreateInfo, nullptr, &renderSemaphore)); - _frame._swapchainSemaphore = std::make_unique(vk_engine->getRawDevice(), swapchainSemaphore); - _frame._renderSemaphore = std::make_unique(vk_engine->getRawDevice(), renderSemaphore); + _frame._swapchainSemaphore = std::make_unique(static_cast(vk_engine->device), swapchainSemaphore); + _frame._renderSemaphore = std::make_unique(static_cast(vk_engine->device), renderSemaphore); } VkFence immFence; - VK_CHECK(vkCreateFence(vk_engine->getRawDevice(), &fenceCreateInfo, nullptr, &immFence)); - _immFence = std::make_unique(vk_engine->getRawDevice(), immFence); + VK_CHECK(vkCreateFence(static_cast(vk_engine->device), &fenceCreateInfo, nullptr, &immFence)); + _immFence = std::make_unique(static_cast(vk_engine->device), immFence); } \ No newline at end of file diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index 6d3d26a5..e793cf0b 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -72,6 +72,21 @@ VulkanEngine::Instance::~Instance() { VulkanEngine::Instance::operator VkInstance() const { return _instance; } VulkanEngine::Instance::operator vkb::Instance() const { return _instance; } +void VulkanEngine::Device::init(const vkb::PhysicalDevice& physical_device) { + const vkb::DeviceBuilder device_builder{physical_device}; + auto dev_ret = device_builder.build(); + if (!dev_ret) { + LOGE("Failed to create logical device. Error: {}", + dev_ret.error().message()); + } + _device = dev_ret.value(); +} + +VulkanEngine::Device::~Device() {vkb::destroy_device(_device);} + +VulkanEngine::Device::operator VkDevice() const { return _device; } +VulkanEngine::Device::operator vkb::Device() const { return _device; } + VKAPI_ATTR VkBool32 VKAPI_CALL VulkanEngine::debugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, @@ -122,7 +137,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanEngine::debugCallback( } void VulkanEngine::destroy_image(const AllocatedImage& img) const { - vkDestroyImageView(getRawDevice(), img.imageView, nullptr); + vkDestroyImageView(static_cast(device), img.imageView, nullptr); vmaDestroyImage(_a._allocator, img.image, img.allocation); } @@ -152,17 +167,17 @@ void VulkanEngine::init_default_data() { const uint32_t white = glm::packUnorm4x8(glm::vec4(1, 1, 1, 1)); AllocatedImage whiteImageData = create_image(&white, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - _whiteImage = std::make_unique(_a._allocator, getRawDevice(), whiteImageData); + _whiteImage = std::make_unique(_a._allocator, static_cast(device), whiteImageData); const uint32_t grey = glm::packUnorm4x8(glm::vec4(0.66f, 0.66f, 0.66f, 1)); AllocatedImage greyImageData = create_image(&grey, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - _greyImage = std::make_unique(_a._allocator, getRawDevice(), greyImageData); + _greyImage = std::make_unique(_a._allocator, static_cast(device), greyImageData); const uint32_t black = glm::packUnorm4x8(glm::vec4(0, 0, 0, 0)); AllocatedImage blackImageData = create_image(&black, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - _blackImage = std::make_unique(_a._allocator, getRawDevice(), blackImageData); + _blackImage = std::make_unique(_a._allocator, static_cast(device), blackImageData); // checkerboard image const uint32_t magenta = glm::packUnorm4x8(glm::vec4(1, 0, 1, 1)); @@ -174,18 +189,18 @@ void VulkanEngine::init_default_data() { } AllocatedImage errorImageData = create_image(pixels.data(), VkExtent3D{16, 16, 1}, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - _errorCheckerboardImage = std::make_unique(_a._allocator, getRawDevice(), errorImageData); + _errorCheckerboardImage = std::make_unique(_a._allocator, static_cast(device), errorImageData); VkSamplerCreateInfo sampl = {.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; sampl.magFilter = VK_FILTER_NEAREST; sampl.minFilter = VK_FILTER_NEAREST; - _defaultSamplerNearest.create(getRawDevice(), &sampl, nullptr); + _defaultSamplerNearest.create(static_cast(device), &sampl, nullptr); sampl.magFilter = VK_FILTER_LINEAR; sampl.minFilter = VK_FILTER_LINEAR; - _defaultSamplerLinear.create(getRawDevice(), &sampl, nullptr); + _defaultSamplerLinear.create(static_cast(device), &sampl, nullptr); GLTFMetallic_Roughness::MaterialResources materialResources{}; // default the material textures @@ -286,23 +301,23 @@ void VulkanEngine::init_descriptors() { {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1}, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1}}; - globalDescriptorAllocator.init(getRawDevice(), 10, sizes); + globalDescriptorAllocator.init(static_cast(device), 10, sizes); // make the descriptor set layout for our compute draw - _drawImageDescriptorLayout.create(getRawDevice(), VK_SHADER_STAGE_COMPUTE_BIT, {{0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE}}); - _gpuSceneDataDescriptorLayout.create(getRawDevice(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, {{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER}}); - _singleImageDescriptorLayout.create(getRawDevice(), VK_SHADER_STAGE_FRAGMENT_BIT, {{0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}}); + _drawImageDescriptorLayout.create(static_cast(device), VK_SHADER_STAGE_COMPUTE_BIT, {{0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE}}); + _gpuSceneDataDescriptorLayout.create(static_cast(device), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, {{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER}}); + _singleImageDescriptorLayout.create(static_cast(device), VK_SHADER_STAGE_FRAGMENT_BIT, {{0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}}); // allocate a descriptor set for our draw image _drawImageDescriptors = globalDescriptorAllocator.allocate( - getRawDevice(), _drawImageDescriptorLayout.set); + static_cast(device), _drawImageDescriptorLayout.set); DescriptorWriter writer; writer.write_image(0, _drawImage->imageView(), VK_NULL_HANDLE, VK_IMAGE_LAYOUT_GENERAL, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE); - writer.update_set(getRawDevice(), _drawImageDescriptors); + writer.update_set(static_cast(device), _drawImageDescriptors); for (auto& _frame : command_buffers_container._frames) { // create a descriptor pool @@ -314,14 +329,14 @@ void VulkanEngine::init_descriptors() { }; _frame._frameDescriptors = DescriptorAllocatorGrowable{}; - _frame._frameDescriptors.init(getRawDevice(), 1000, frame_sizes); + _frame._frameDescriptors.init(static_cast(device), 1000, frame_sizes); // No need for deletion queue - frame descriptors will be cleaned up in cleanup() } } void VulkanEngine::init_pipelines() { - pipelines.init(getRawDevice(), _singleImageDescriptorLayout.set, _drawImageDescriptorLayout.set, _drawImage->get()); + pipelines.init(static_cast(device), _singleImageDescriptorLayout.set, _drawImageDescriptorLayout.set, _drawImage->get()); // Pipeline cleanup is handled automatically by the Pipelines object metalRoughMaterial.build_pipelines(this); } @@ -338,7 +353,7 @@ VulkanEngine::VulkanEngine(Camera& camera) : mainCamera(&camera) { command_buffers_container.init_sync_structures(this); init_descriptors(); init_pipelines(); - _imgui.init(getRawDevice(), _window.ptr, static_cast(instance), _chosenGPU, _graphicsQueue, &_swapchainImageFormat); + _imgui.init(static_cast(device), _window.ptr, static_cast(instance), _chosenGPU, _graphicsQueue, &_swapchainImageFormat); init_default_data(); mainCamera->velocity = glm::vec3(0.f); @@ -414,10 +429,10 @@ void VulkanEngine::init_vulkan() { const vkb::PhysicalDevice& physical_device = physical_device_ret.value(); // Get the VkDevice handle used in the rest of a vulkan application - _device.init(physical_device); + device.init(physical_device); _chosenGPU = physical_device.physical_device; - auto queue_ret = _device.device.get_queue(vkb::QueueType::graphics); + auto queue_ret = static_cast(device).get_queue(vkb::QueueType::graphics); if (!queue_ret) { LOGE("Failed to retrieve graphics queue. Error: {}", queue_ret.error().message()); @@ -425,7 +440,7 @@ void VulkanEngine::init_vulkan() { _graphicsQueue = queue_ret.value(); - auto queue_family_ret = _device.device.get_queue_index(vkb::QueueType::graphics); + auto queue_family_ret = static_cast(device).get_queue_index(vkb::QueueType::graphics); if (!queue_family_ret) { LOGE("Failed to retrieve graphics queue family. Error: {}", queue_family_ret.error().message()); @@ -436,7 +451,7 @@ void VulkanEngine::init_vulkan() { // initialize the memory allocator VmaAllocatorCreateInfo allocatorInfo = {}; allocatorInfo.physicalDevice = _chosenGPU; - allocatorInfo.device = getRawDevice(); + allocatorInfo.device = static_cast(device); allocatorInfo.instance = static_cast(instance); allocatorInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; // // vmaCreateAllocator(&allocatorInfo, &_a._allocator); @@ -447,7 +462,7 @@ void VulkanEngine::init_vulkan() { void VulkanEngine::create_swapchain(uint32_t width, uint32_t height) { - vkb::SwapchainBuilder swapchainBuilder{_chosenGPU, getRawDevice(), _surface}; + vkb::SwapchainBuilder swapchainBuilder{_chosenGPU, static_cast(device), _surface}; _swapchainImageFormat = VK_FORMAT_B8G8R8A8_UNORM; @@ -515,11 +530,11 @@ void VulkanEngine::init_swapchain() { drawImageFormat, drawImageData.image, VK_IMAGE_ASPECT_COLOR_BIT); - VK_CHECK(vkCreateImageView(getRawDevice(), &rview_info, nullptr, + VK_CHECK(vkCreateImageView(static_cast(device), &rview_info, nullptr, &drawImageData.imageView)); // Create smart pointer for automatic cleanup - _drawImage = std::make_unique(_a._allocator, getRawDevice(), drawImageData); + _drawImage = std::make_unique(_a._allocator, static_cast(device), drawImageData); // Create depth image VkExtent3D depthImageExtent = { @@ -552,19 +567,19 @@ void VulkanEngine::init_swapchain() { VkImageViewCreateInfo dview_info = vkinit::imageview_create_info( depthFormat, depthImageData.image, VK_IMAGE_ASPECT_DEPTH_BIT); - VK_CHECK(vkCreateImageView(getRawDevice(), &dview_info, nullptr, + VK_CHECK(vkCreateImageView(static_cast(device), &dview_info, nullptr, &depthImageData.imageView)); // Create smart pointer for automatic cleanup - _depthImage = std::make_unique(_a._allocator, getRawDevice(), depthImageData); + _depthImage = std::make_unique(_a._allocator, static_cast(device), depthImageData); } void VulkanEngine::destroy_swapchain() { - vkDestroySwapchainKHR(getRawDevice(), _swapchain, nullptr); + vkDestroySwapchainKHR(static_cast(device), _swapchain, nullptr); // destroy swapchain resources for (const auto& _swapchainImageView : _swapchainImageViews) { - vkDestroyImageView(getRawDevice(), _swapchainImageView, nullptr); + vkDestroyImageView(static_cast(device), _swapchainImageView, nullptr); } } @@ -572,7 +587,7 @@ void VulkanEngine::destroy_swapchain() { { if (_isInitialized) { // make sure the gpu has stopped doing its things - vkDeviceWaitIdle(getRawDevice()); + vkDeviceWaitIdle(static_cast(device)); loadedScenes.clear(); @@ -580,9 +595,9 @@ void VulkanEngine::destroy_swapchain() { for (auto& _frame : command_buffers_container._frames) { // Destroy frame descriptors manually - _frame._frameDescriptors.destroy_pools(getRawDevice()); + _frame._frameDescriptors.destroy_pools(static_cast(device)); } - globalDescriptorAllocator.destroy_pools(getRawDevice()); + globalDescriptorAllocator.destroy_pools(static_cast(device)); destroy_swapchain(); @@ -641,7 +656,7 @@ GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, .buffer = newSurface.vertexBuffer.buffer}; newSurface.vertexBufferAddress = - vkGetBufferDeviceAddress(getRawDevice(), &deviceAddressInfo); + vkGetBufferDeviceAddress(static_cast(device), &deviceAddressInfo); // create index buffer newSurface.indexBuffer = create_buffer( @@ -734,12 +749,12 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { // create a descriptor set that binds that buffer and update it VkDescriptorSet globalDescriptor = get_current_frame()._frameDescriptors.allocate( - getRawDevice(), _gpuSceneDataDescriptorLayout.set); + static_cast(device), _gpuSceneDataDescriptorLayout.set); DescriptorWriter writer; writer.write_buffer(0, gpuSceneDataBuffer.buffer, sizeof(GPUSceneData), 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); - writer.update_set(getRawDevice(), globalDescriptor); + writer.update_set(static_cast(device), globalDescriptor); VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info( _drawImage->imageView(), nullptr, VK_IMAGE_LAYOUT_GENERAL); @@ -771,13 +786,13 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { // bind a texture VkDescriptorSet imageSet = get_current_frame()._frameDescriptors.allocate( - getRawDevice(), _singleImageDescriptorLayout.set); + static_cast(device), _singleImageDescriptorLayout.set); DescriptorWriter single_image_writer; single_image_writer.write_image(0, _errorCheckerboardImage->imageView(), _defaultSamplerNearest.sampler, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); - single_image_writer.update_set(getRawDevice(), imageSet); + single_image_writer.update_set(static_cast(device), imageSet); pipelines.meshPipeline->bindDescriptorSets(cmd, &imageSet, 1); @@ -812,19 +827,19 @@ void VulkanEngine::draw() { // wait until the gpu has finished rendering the last frame. Timeout of 1 // second - VK_CHECK(vkWaitForFences(getRawDevice(), 1, get_current_frame()._renderFence->getPtr(), + VK_CHECK(vkWaitForFences(static_cast(device), 1, get_current_frame()._renderFence->getPtr(), true, 1000000000)); // Clear frame buffers instead of flushing deletion queue get_current_frame()._frameBuffers.clear(); - get_current_frame()._frameDescriptors.clear_pools(getRawDevice()); + get_current_frame()._frameDescriptors.clear_pools(static_cast(device)); - VK_CHECK(vkResetFences(getRawDevice(), 1, get_current_frame()._renderFence->getPtr())); + VK_CHECK(vkResetFences(static_cast(device), 1, get_current_frame()._renderFence->getPtr())); // request image from the swapchain uint32_t swapchainImageIndex; const VkResult e = - vkAcquireNextImageKHR(getRawDevice(), _swapchain, 1000000000, + vkAcquireNextImageKHR(static_cast(device), _swapchain, 1000000000, get_current_frame()._swapchainSemaphore->get(), nullptr, &swapchainImageIndex); if (e == VK_ERROR_OUT_OF_DATE_KHR) { @@ -949,7 +964,7 @@ void VulkanEngine::draw() { } void VulkanEngine::resize_swapchain() { - vkDeviceWaitIdle(getRawDevice()); + vkDeviceWaitIdle(static_cast(device)); destroy_swapchain(); @@ -1007,7 +1022,7 @@ AllocatedImage VulkanEngine::create_image(VkExtent3D size, VkFormat format, vkinit::imageview_create_info(format, newImage.image, aspectFlag); view_info.subresourceRange.levelCount = img_info.mipLevels; - VK_CHECK(vkCreateImageView(getRawDevice(), &view_info, nullptr, + VK_CHECK(vkCreateImageView(static_cast(device), &view_info, nullptr, &newImage.imageView)); return newImage; diff --git a/src/graphics/vulkan/vk_loader.cpp b/src/graphics/vulkan/vk_loader.cpp index 86c1e75e..76ac04d8 100644 --- a/src/graphics/vulkan/vk_loader.cpp +++ b/src/graphics/vulkan/vk_loader.cpp @@ -251,7 +251,7 @@ std::optional> loadGltf(VulkanEngine* engine, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3}, {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}}; file.descriptorPool.init( - engine->getRawDevice(), + static_cast(engine->device), static_cast(std::max(gltf.materials.size(), size_t(1))), sizes); @@ -269,7 +269,7 @@ std::optional> loadGltf(VulkanEngine* engine, sampl.mipmapMode = extract_mipmap_mode( sampler.minFilter.value_or(fastgltf::Filter::Nearest)); VkSampler newSampler; - vkCreateSampler(engine->getRawDevice(), &sampl, nullptr, &newSampler); + vkCreateSampler(static_cast(engine->device), &sampl, nullptr, &newSampler); file.samplers.push_back(newSampler); } @@ -524,7 +524,7 @@ void LoadedGLTF::Draw(const glm::mat4& topMatrix, DrawContext& ctx) { } void LoadedGLTF::clearAll() { - VkDevice dv = creator->getRawDevice(); + VkDevice dv = static_cast(creator->device); descriptorPool.destroy_pools(dv); creator->destroy_buffer(materialDataBuffer); // for (auto& [k, v] : meshes) { diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 861a318f..acd9a044 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -77,20 +77,15 @@ class VulkanEngine { Instance instance; struct Device { - vkb::Device device; - void init(const vkb::PhysicalDevice& physical_device) { - const vkb::DeviceBuilder device_builder{physical_device}; - auto dev_ret = device_builder.build(); - if (!dev_ret) { - LOGE("Failed to create logical device. Error: {}", - dev_ret.error().message()); - } - device = dev_ret.value(); - } - ~Device() {vkb::destroy_device(device);} + explicit operator VkDevice() const; + explicit operator vkb::Device() const; + void init(const vkb::PhysicalDevice& physical_device); + ~Device(); + private: + vkb::Device _device; }; - Device _device; - VkDevice getRawDevice() const { return _device.device; } + Device device; + struct Allocator { VmaAllocator _allocator{nullptr}; void init(VmaAllocatorCreateInfo info) {vmaCreateAllocator(&info, &_allocator);} // move info inside From d09a03f4b60616fa52bef7027f69e3aa9f2f3d0c Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:53:56 +0300 Subject: [PATCH 20/24] refactor: Allocator --- src/graphics/vulkan/vk_engine.cpp | 60 ++++++++++++++----------- src/include/graphics/vulkan/vk_engine.h | 10 +++-- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index e793cf0b..ca8aea28 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -87,6 +87,24 @@ VulkanEngine::Device::~Device() {vkb::destroy_device(_device);} VulkanEngine::Device::operator VkDevice() const { return _device; } VulkanEngine::Device::operator vkb::Device() const { return _device; } +void VulkanEngine::Allocator::init(const VkPhysicalDevice& pGpu, + const VkDevice& pDevice, + const VkInstance& pInstance) { + const VmaAllocatorCreateInfo info = { + .flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT, + .physicalDevice = pGpu, + .device = pDevice, + .instance = pInstance + }; + vmaCreateAllocator(&info, &_allocator); +} + +VulkanEngine::Allocator::~Allocator() { + vmaDestroyAllocator(_allocator); +} + +VulkanEngine::Allocator::operator VmaAllocator() const { return _allocator; } + VKAPI_ATTR VkBool32 VKAPI_CALL VulkanEngine::debugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, @@ -138,7 +156,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanEngine::debugCallback( void VulkanEngine::destroy_image(const AllocatedImage& img) const { vkDestroyImageView(static_cast(device), img.imageView, nullptr); - vmaDestroyImage(_a._allocator, img.image, img.allocation); + vmaDestroyImage(static_cast(allocator), img.image, img.allocation); } void VulkanEngine::init_default_data() { @@ -167,17 +185,17 @@ void VulkanEngine::init_default_data() { const uint32_t white = glm::packUnorm4x8(glm::vec4(1, 1, 1, 1)); AllocatedImage whiteImageData = create_image(&white, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - _whiteImage = std::make_unique(_a._allocator, static_cast(device), whiteImageData); + _whiteImage = std::make_unique(static_cast(allocator), static_cast(device), whiteImageData); const uint32_t grey = glm::packUnorm4x8(glm::vec4(0.66f, 0.66f, 0.66f, 1)); AllocatedImage greyImageData = create_image(&grey, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - _greyImage = std::make_unique(_a._allocator, static_cast(device), greyImageData); + _greyImage = std::make_unique(static_cast(allocator), static_cast(device), greyImageData); const uint32_t black = glm::packUnorm4x8(glm::vec4(0, 0, 0, 0)); AllocatedImage blackImageData = create_image(&black, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - _blackImage = std::make_unique(_a._allocator, static_cast(device), blackImageData); + _blackImage = std::make_unique(static_cast(allocator), static_cast(device), blackImageData); // checkerboard image const uint32_t magenta = glm::packUnorm4x8(glm::vec4(1, 0, 1, 1)); @@ -189,7 +207,7 @@ void VulkanEngine::init_default_data() { } AllocatedImage errorImageData = create_image(pixels.data(), VkExtent3D{16, 16, 1}, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - _errorCheckerboardImage = std::make_unique(_a._allocator, static_cast(device), errorImageData); + _errorCheckerboardImage = std::make_unique(static_cast(allocator), static_cast(device), errorImageData); VkSamplerCreateInfo sampl = {.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; @@ -222,7 +240,7 @@ void VulkanEngine::init_default_data() { sceneUniformData->metal_rough_factors = glm::vec4{1, 0.5, 0, 0}; // Store material constants buffer in managed buffers for automatic cleanup - _managedBuffers.push_back(std::make_unique(_a._allocator, materialConstants)); + _managedBuffers.push_back(std::make_unique(static_cast(allocator), materialConstants)); materialResources.dataBuffer = materialConstants.buffer; materialResources.dataBufferOffset = 0; @@ -447,15 +465,7 @@ void VulkanEngine::init_vulkan() { } _graphicsQueueFamily = queue_family_ret.value(); - - // initialize the memory allocator - VmaAllocatorCreateInfo allocatorInfo = {}; - allocatorInfo.physicalDevice = _chosenGPU; - allocatorInfo.device = static_cast(device); - allocatorInfo.instance = static_cast(instance); - allocatorInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; - // // vmaCreateAllocator(&allocatorInfo, &_a._allocator); - _a.init(allocatorInfo); + allocator.init(_chosenGPU, static_cast(device), static_cast(instance)); // VMA allocator will be destroyed in cleanup() - no need for deletion queue } @@ -522,7 +532,7 @@ void VulkanEngine::init_swapchain() { drawImageData.imageExtent = drawImageExtent; // allocate and create the image - vmaCreateImage(_a._allocator, &rimg_info, &rimg_allocinfo, &drawImageData.image, + vmaCreateImage(static_cast(allocator), &rimg_info, &rimg_allocinfo, &drawImageData.image, &drawImageData.allocation, nullptr); // build an image-view for the draw image to use for rendering @@ -534,7 +544,7 @@ void VulkanEngine::init_swapchain() { &drawImageData.imageView)); // Create smart pointer for automatic cleanup - _drawImage = std::make_unique(_a._allocator, static_cast(device), drawImageData); + _drawImage = std::make_unique(static_cast(allocator), static_cast(device), drawImageData); // Create depth image VkExtent3D depthImageExtent = { @@ -560,7 +570,7 @@ void VulkanEngine::init_swapchain() { depthImageData.imageExtent = depthImageExtent; // allocate and create the depth image - vmaCreateImage(_a._allocator, &dimg_info, &dimg_allocinfo, &depthImageData.image, + vmaCreateImage(static_cast(allocator), &dimg_info, &dimg_allocinfo, &depthImageData.image, &depthImageData.allocation, nullptr); // build an image-view for the depth image @@ -571,7 +581,7 @@ void VulkanEngine::init_swapchain() { &depthImageData.imageView)); // Create smart pointer for automatic cleanup - _depthImage = std::make_unique(_a._allocator, static_cast(device), depthImageData); + _depthImage = std::make_unique(static_cast(allocator), static_cast(device), depthImageData); } void VulkanEngine::destroy_swapchain() { @@ -625,7 +635,7 @@ AllocatedBuffer VulkanEngine::create_buffer(size_t allocSize, AllocatedBuffer newBuffer{}; // allocate the buffer - VK_CHECK(vmaCreateBuffer(_a._allocator, &bufferInfo, &vmallocinfo, + VK_CHECK(vmaCreateBuffer(static_cast(allocator), &bufferInfo, &vmallocinfo, &newBuffer.buffer, &newBuffer.allocation, &newBuffer.info)); @@ -633,7 +643,7 @@ AllocatedBuffer VulkanEngine::create_buffer(size_t allocSize, } void VulkanEngine::destroy_buffer(const AllocatedBuffer& buffer) const { - vmaDestroyBuffer(_a._allocator, buffer.buffer, buffer.allocation); + vmaDestroyBuffer(static_cast(allocator), buffer.buffer, buffer.allocation); } GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, @@ -695,8 +705,8 @@ GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, this); // Store mesh buffers in managed collections for automatic cleanup - _managedBuffers.push_back(std::make_unique(_a._allocator, newSurface.vertexBuffer)); - _managedBuffers.push_back(std::make_unique(_a._allocator, newSurface.indexBuffer)); + _managedBuffers.push_back(std::make_unique(static_cast(allocator), newSurface.vertexBuffer)); + _managedBuffers.push_back(std::make_unique(static_cast(allocator), newSurface.indexBuffer)); destroy_buffer(staging); return newSurface; @@ -739,7 +749,7 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { // add it to the current frame's managed buffers for automatic cleanup get_current_frame()._frameBuffers.push_back( - std::make_unique(_a._allocator, gpuSceneDataBuffer)); + std::make_unique(static_cast(allocator), gpuSceneDataBuffer)); // write the buffer auto* sceneUniformData = @@ -1007,7 +1017,7 @@ AllocatedImage VulkanEngine::create_image(VkExtent3D size, VkFormat format, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); // allocate and create the image - VK_CHECK(vmaCreateImage(_a._allocator, &img_info, &allocinfo, &newImage.image, + VK_CHECK(vmaCreateImage(static_cast(allocator), &img_info, &allocinfo, &newImage.image, &newImage.allocation, nullptr)); // if the format is a depth format, we will need to have it use the correct diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index acd9a044..30f33792 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -87,11 +87,13 @@ class VulkanEngine { Device device; struct Allocator { - VmaAllocator _allocator{nullptr}; - void init(VmaAllocatorCreateInfo info) {vmaCreateAllocator(&info, &_allocator);} // move info inside - ~Allocator() {vmaDestroyAllocator(_allocator);} + explicit operator VmaAllocator() const; + void init(const VkPhysicalDevice& gpu, const VkDevice& device, const VkInstance& instance); + ~Allocator(); + private: + VmaAllocator _allocator{VK_NULL_HANDLE}; }; - Allocator _a; // todo: rename + Allocator allocator; Pipelines pipelines; From 26ed2e2c6fac5998f4b63586004107602c3d2231 Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:55:34 +0300 Subject: [PATCH 21/24] refactor: take Device out of VulkanEngine --- src/graphics/CMakeLists.txt | 1 + src/graphics/vulkan/Device.cpp | 25 +++++++++++++++++++++++++ src/graphics/vulkan/vk_engine.cpp | 15 --------------- src/include/graphics/vulkan/Device.h | 13 +++++++++++++ src/include/graphics/vulkan/vk_engine.h | 12 ++---------- 5 files changed, 41 insertions(+), 25 deletions(-) create mode 100644 src/graphics/vulkan/Device.cpp create mode 100644 src/include/graphics/vulkan/Device.h diff --git a/src/graphics/CMakeLists.txt b/src/graphics/CMakeLists.txt index b1c1e7f3..968a9e24 100644 --- a/src/graphics/CMakeLists.txt +++ b/src/graphics/CMakeLists.txt @@ -11,6 +11,7 @@ target_sources(${PROJECT_NAME} vulkan/pipelines.cpp vulkan/ComputePipeline.cpp vulkan/DescriptorSetLayout.cpp + vulkan/Device.cpp vulkan/GraphicsPipeline.cpp Graphics.cpp ) \ No newline at end of file diff --git a/src/graphics/vulkan/Device.cpp b/src/graphics/vulkan/Device.cpp new file mode 100644 index 00000000..e1a90814 --- /dev/null +++ b/src/graphics/vulkan/Device.cpp @@ -0,0 +1,25 @@ +#include "graphics/vulkan/Device.h" + +#include "core/Logging.h" + +void Device::init(const vkb::PhysicalDevice& physical_device) { + const vkb::DeviceBuilder device_builder{physical_device}; + auto dev_ret = device_builder.build(); + if (!dev_ret) { + LOGE("Failed to create logical device. Error: {}", + dev_ret.error().message()); + } + _device = dev_ret.value(); +} + +Device::~Device() { + vkb::destroy_device(_device); +} + +Device::operator VkDevice() const { + return _device; +} + +Device::operator vkb::Device() const { + return _device; +} \ No newline at end of file diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index ca8aea28..0fc294e5 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -72,21 +72,6 @@ VulkanEngine::Instance::~Instance() { VulkanEngine::Instance::operator VkInstance() const { return _instance; } VulkanEngine::Instance::operator vkb::Instance() const { return _instance; } -void VulkanEngine::Device::init(const vkb::PhysicalDevice& physical_device) { - const vkb::DeviceBuilder device_builder{physical_device}; - auto dev_ret = device_builder.build(); - if (!dev_ret) { - LOGE("Failed to create logical device. Error: {}", - dev_ret.error().message()); - } - _device = dev_ret.value(); -} - -VulkanEngine::Device::~Device() {vkb::destroy_device(_device);} - -VulkanEngine::Device::operator VkDevice() const { return _device; } -VulkanEngine::Device::operator vkb::Device() const { return _device; } - void VulkanEngine::Allocator::init(const VkPhysicalDevice& pGpu, const VkDevice& pDevice, const VkInstance& pInstance) { diff --git a/src/include/graphics/vulkan/Device.h b/src/include/graphics/vulkan/Device.h new file mode 100644 index 00000000..4ae8eebe --- /dev/null +++ b/src/include/graphics/vulkan/Device.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include + +struct Device { + explicit operator VkDevice() const; + explicit operator vkb::Device() const; + void init(const vkb::PhysicalDevice& physical_device); + ~Device(); + +private: + vkb::Device _device; +}; diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 30f33792..ff418d89 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -11,13 +12,13 @@ #include #include #include -#include #include #include #include #include "ComputePipeline.h" #include "DescriptorSetLayout.h" +#include "Device.h" #include "core/ModelImpl.h" #include "pipelines.h" #include "vk_command_buffers.h" @@ -75,15 +76,6 @@ class VulkanEngine { vkb::Instance _instance; }; Instance instance; - - struct Device { - explicit operator VkDevice() const; - explicit operator vkb::Device() const; - void init(const vkb::PhysicalDevice& physical_device); - ~Device(); - private: - vkb::Device _device; - }; Device device; struct Allocator { From d39d22e13e6385501888b50f0e773846ac26a879 Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:56:54 +0300 Subject: [PATCH 22/24] refactor: Sampler --- src/graphics/vulkan/vk_engine.cpp | 20 +++++++++++++++++--- src/graphics/vulkan/vk_loader.cpp | 8 ++++---- src/include/graphics/vulkan/vk_engine.h | 18 +++++++----------- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index 0fc294e5..50773489 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -90,6 +90,20 @@ VulkanEngine::Allocator::~Allocator() { VulkanEngine::Allocator::operator VmaAllocator() const { return _allocator; } +void VulkanEngine::Sampler::create(const VkDevice& pDevice, + const VkSamplerCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + _device = pDevice; + _allocator = pAllocator; + vkCreateSampler(_device, pCreateInfo, pAllocator, &_sampler); +} + +VulkanEngine::Sampler::~Sampler() { + vkDestroySampler(_device, _sampler, _allocator); +} + +VulkanEngine::Sampler::operator VkSampler() const { return _sampler; } + VKAPI_ATTR VkBool32 VKAPI_CALL VulkanEngine::debugCallback( VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, @@ -208,9 +222,9 @@ void VulkanEngine::init_default_data() { GLTFMetallic_Roughness::MaterialResources materialResources{}; // default the material textures materialResources.colorImage = _whiteImage->get(); - materialResources.colorSampler = _defaultSamplerLinear.sampler; + materialResources.colorSampler = static_cast(_defaultSamplerLinear); materialResources.metalRoughImage = _whiteImage->get(); - materialResources.metalRoughSampler = _defaultSamplerLinear.sampler; + materialResources.metalRoughSampler = static_cast(_defaultSamplerLinear); // set the uniform buffer for the material data const AllocatedBuffer materialConstants = create_buffer( @@ -784,7 +798,7 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { static_cast(device), _singleImageDescriptorLayout.set); DescriptorWriter single_image_writer; single_image_writer.write_image(0, _errorCheckerboardImage->imageView(), - _defaultSamplerNearest.sampler, + static_cast(_defaultSamplerNearest), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); single_image_writer.update_set(static_cast(device), imageSet); diff --git a/src/graphics/vulkan/vk_loader.cpp b/src/graphics/vulkan/vk_loader.cpp index 76ac04d8..39f70274 100644 --- a/src/graphics/vulkan/vk_loader.cpp +++ b/src/graphics/vulkan/vk_loader.cpp @@ -319,9 +319,9 @@ std::optional> loadGltf(VulkanEngine* engine, GLTFMetallic_Roughness::MaterialResources materialResources; materialResources.colorImage = engine->_whiteImage->get(); - materialResources.colorSampler = engine->_defaultSamplerLinear.sampler; + materialResources.colorSampler = static_cast(engine->_defaultSamplerLinear); materialResources.metalRoughImage = engine->_whiteImage->get(); - materialResources.metalRoughSampler = engine->_defaultSamplerLinear.sampler; + materialResources.metalRoughSampler = static_cast(engine->_defaultSamplerLinear); materialResources.dataBuffer = file.materialDataBuffer.buffer; materialResources.dataBufferOffset = data_index * sizeof(GLTFMetallic_Roughness::MaterialConstants); @@ -356,9 +356,9 @@ std::optional> loadGltf(VulkanEngine* engine, GLTFMetallic_Roughness::MaterialResources resources; resources.colorImage = engine->_whiteImage->get(); - resources.colorSampler = engine->_defaultSamplerLinear.sampler; + resources.colorSampler = static_cast(engine->_defaultSamplerLinear); resources.metalRoughImage = engine->_whiteImage->get(); - resources.metalRoughSampler = engine->_defaultSamplerLinear.sampler; + resources.metalRoughSampler = static_cast(engine->_defaultSamplerLinear); resources.dataBuffer = file.materialDataBuffer.buffer; resources.dataBufferOffset = 0; diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index ff418d89..5af04689 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -180,19 +180,15 @@ class VulkanEngine { std::unique_ptr _errorCheckerboardImage; struct Sampler { - VkSampler sampler{VK_NULL_HANDLE}; - VkDevice device{VK_NULL_HANDLE}; - const VkAllocationCallbacks* allocator{VK_NULL_HANDLE}; + explicit operator VkSampler() const; void create(const VkDevice& pDevice, const VkSamplerCreateInfo* pCreateInfo, - const VkAllocationCallbacks* pAllocator) { - device = pDevice; - allocator = pAllocator; - vkCreateSampler(device, pCreateInfo, pAllocator, &sampler); - } - ~Sampler() { - vkDestroySampler(device, sampler, allocator); - } + const VkAllocationCallbacks* pAllocator); + ~Sampler(); + private: + VkSampler _sampler{VK_NULL_HANDLE}; + VkDevice _device{VK_NULL_HANDLE}; + const VkAllocationCallbacks* _allocator{VK_NULL_HANDLE}; }; Sampler _defaultSamplerLinear; Sampler _defaultSamplerNearest; From 43eb8898bdbcb1279eca3826726cc0e91b93cadb Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:57:46 +0300 Subject: [PATCH 23/24] chore: nullptr->VK_NULL_HANDLE --- src/include/graphics/vulkan/vk_engine.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 5af04689..9434e60e 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -220,8 +220,8 @@ class VulkanEngine { ~Imgui(); private: void initImguiPool(); - VkDevice _device{nullptr}; - VkDescriptorPool _imguiPool{nullptr}; + VkDevice _device{VK_NULL_HANDLE}; + VkDescriptorPool _imguiPool{VK_NULL_HANDLE}; }; Imgui _imgui; // Smart pointer collections for automatic cleanup From b4c4c244186a4a3fe65c047ff87d2f7abf00d738 Mon Sep 17 00:00:00 2001 From: Varnill Date: Sun, 28 Sep 2025 23:59:10 +0300 Subject: [PATCH 24/24] refactor: move init_vulkan to VulkanInit class The change also made it possible to switch from init() methods to constructors. --- src/graphics/CMakeLists.txt | 1 + src/graphics/vulkan/Device.cpp | 57 ++-- src/graphics/vulkan/VulkanInit.cpp | 164 ++++++++++ src/graphics/vulkan/pipelines.cpp | 16 +- src/graphics/vulkan/vk_command_buffers.cpp | 20 +- .../vulkan/vk_command_buffers_container.cpp | 16 +- src/graphics/vulkan/vk_engine.cpp | 280 ++++-------------- src/graphics/vulkan/vk_loader.cpp | 6 +- src/include/graphics/vulkan/Device.h | 9 +- src/include/graphics/vulkan/VulkanInit.h | 64 ++++ src/include/graphics/vulkan/vk_engine.h | 44 +-- 11 files changed, 357 insertions(+), 320 deletions(-) create mode 100644 src/graphics/vulkan/VulkanInit.cpp create mode 100644 src/include/graphics/vulkan/VulkanInit.h diff --git a/src/graphics/CMakeLists.txt b/src/graphics/CMakeLists.txt index 968a9e24..32a4feb1 100644 --- a/src/graphics/CMakeLists.txt +++ b/src/graphics/CMakeLists.txt @@ -13,5 +13,6 @@ target_sources(${PROJECT_NAME} vulkan/DescriptorSetLayout.cpp vulkan/Device.cpp vulkan/GraphicsPipeline.cpp + vulkan/VulkanInit.cpp Graphics.cpp ) \ No newline at end of file diff --git a/src/graphics/vulkan/Device.cpp b/src/graphics/vulkan/Device.cpp index e1a90814..8349f442 100644 --- a/src/graphics/vulkan/Device.cpp +++ b/src/graphics/vulkan/Device.cpp @@ -2,24 +2,45 @@ #include "core/Logging.h" -void Device::init(const vkb::PhysicalDevice& physical_device) { - const vkb::DeviceBuilder device_builder{physical_device}; - auto dev_ret = device_builder.build(); - if (!dev_ret) { - LOGE("Failed to create logical device. Error: {}", - dev_ret.error().message()); - } - _device = dev_ret.value(); -} +Device::Device(const vkb::Instance& instance, const VkSurfaceKHR surface) + : physical(VK_NULL_HANDLE), logical([&] { + VkPhysicalDeviceVulkan13Features features{ + .synchronization2 = true, + .dynamicRendering = true, + }; -Device::~Device() { - vkb::destroy_device(_device); -} + VkPhysicalDeviceVulkan12Features features12{ + .descriptorIndexing = true, + .bufferDeviceAddress = true, + }; -Device::operator VkDevice() const { - return _device; -} + // use vkbootstrap to select a gpu. + // We want a gpu that can write to the SDL surface and supports + // vulkan 1.3 with the correct features + vkb::PhysicalDeviceSelector selector{instance}; + + auto res = selector.set_minimum_version(1, 3) + .set_required_features_13(features) + .set_required_features_12(features12) + .set_surface(surface) + .select(); + if (!res) { + LOGE("Failed to select physical device. Error: {}", + res.error().message()); + } -Device::operator vkb::Device() const { - return _device; -} \ No newline at end of file + const vkb::PhysicalDevice& physical_device = res.value(); + + const vkb::DeviceBuilder device_builder{physical_device}; + auto dv_res = device_builder.build(); + if (!dv_res) { + LOGE("Failed to create logical device. Error: {}", + dv_res.error().message()); + } + physical = physical_device.physical_device; + return dv_res.value(); + }()) {} + +Device::~Device() { + vkb::destroy_device(logical); +} diff --git a/src/graphics/vulkan/VulkanInit.cpp b/src/graphics/vulkan/VulkanInit.cpp new file mode 100644 index 00000000..95f1d34b --- /dev/null +++ b/src/graphics/vulkan/VulkanInit.cpp @@ -0,0 +1,164 @@ +#include "graphics/vulkan/VulkanInit.h" + +#include +#include +#include + +#include "core/Logging.h" +#include "graphics/vulkan/Device.h" + +namespace vkb { +struct Device; +} + +#ifdef NDEBUG +constexpr bool bUseValidationLayers = false; +#else +constexpr bool bUseValidationLayers = true; +#endif + +VulkanInit::Window::Window() : window{SDL_CreateWindow("engine", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, 1700, 900, SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE)} {} +VulkanInit::Window::~Window() { SDL_DestroyWindow(window); } + +VulkanInit::Surface::Surface(SDL_Window *window, const VkInstance& instance) { + const SDL_bool err = SDL_Vulkan_CreateSurface(window, instance, &surface); + if (err == SDL_FALSE) { + LOGE("Failed to create Vulkan surface. Error: {}", SDL_GetError()); + } + _instance = instance; +} + +VulkanInit::Surface::~Surface() { + vkDestroySurfaceKHR(_instance, surface, nullptr); +} + +VulkanInit::Queue::Queue(const vkb::Device& dv) + : queue([&] { + auto res = dv.get_queue(vkb::QueueType::graphics); + if (!res) { + LOGE("Failed to retrieve graphics queue. Error: {}", + res.error().message()); + } + return res.value(); + }()), + index([&] { + auto queue_family_ret = dv.get_queue_index(vkb::QueueType::graphics); + if (!queue_family_ret) { + LOGE("Failed to retrieve graphics queue family. Error: {}", + queue_family_ret.error().message()); + } + + return queue_family_ret.value(); + }()) {} + +VulkanInit::VulkanInit() + : surface(window.window, getInstance()) + , device(instance.instance, getSurface()) + , queue(device.logical) + , allocator(getPhysicalDevice(), device.logical, getInstance()){} + +VulkanInit::Instance::Instance() : instance([] { + vkb::InstanceBuilder builder; + + auto system_info_ret = vkb::SystemInfo::get_system_info(); + if (!system_info_ret) { + LOGE("Failed to retrieve system info. Error: {}", + system_info_ret.error().message()); + } + + auto system_info = system_info_ret.value(); + + LOGI("Available layers:") + + for (auto& layer : system_info.available_layers) { + LOGI(layer.layerName); + } + + LOGI("Available extensions:") + + for (auto& [extensionName, _] : system_info.available_extensions) { + LOGI(extensionName); + } + + auto inst_ret = builder.set_app_name("TODO: PUT APP NAME HERE") + .set_engine_name("rainsystem") + .request_validation_layers(bUseValidationLayers) + .set_debug_callback(debugCallback) + .require_api_version(1, 3, 0) + .build(); + if (!inst_ret) { + LOGE("Failed to create Vulkan instance. Error: {}", + inst_ret.error().message()); + } + return inst_ret.value(); +}()){} + +VulkanInit::Instance::~Instance() { + vkb::destroy_instance(instance); +} + +VulkanInit::Allocator::Allocator(const VkPhysicalDevice& pGpu, + const VkDevice& pDevice, + const VkInstance& pInstance) { + const VmaAllocatorCreateInfo info = { + .flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT, + .physicalDevice = pGpu, + .device = pDevice, + .instance = pInstance + }; + vmaCreateAllocator(&info, &allocator); +} + +VulkanInit::Allocator::~Allocator() { + vmaDestroyAllocator(allocator); +} + +VKAPI_ATTR VkBool32 VKAPI_CALL VulkanInit::debugCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + [[maybe_unused]] void* pUserData) { + std::string type; + + switch (messageType) { + case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT: + type = "General"; + + break; + + case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT: + type = "Validation"; + + break; + + case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT: + type = "Performance"; + + break; + case VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT: + type = "Modified set of GPU-visible virtual addresses"; + break; + default: + type = "Unknown"; + } + + std::string message = "(" + type + ")" + pCallbackData->pMessage; + + if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) { + LOGD(message) + } else if (messageSeverity == + VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) { + LOGI(message) + } else if (messageSeverity == + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { + LOGW(message) + } else if (messageSeverity >= + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { + LOGE("{}", message) + } else { + LOGE("{}", message) + } + + return VK_FALSE; +} \ No newline at end of file diff --git a/src/graphics/vulkan/pipelines.cpp b/src/graphics/vulkan/pipelines.cpp index 0e2c66b2..3994d9a4 100644 --- a/src/graphics/vulkan/pipelines.cpp +++ b/src/graphics/vulkan/pipelines.cpp @@ -9,7 +9,7 @@ void GLTFMetallic_Roughness::build_pipelines(VulkanEngine* engine) { create_material_layout(engine); VkPipelineLayout newLayout = create_pipeline_layout(engine); - device = static_cast(engine->device); + device = engine->getLogicalDevice(); opaquePipeline.layout = newLayout; transparentPipeline.layout = newLayout; @@ -17,15 +17,15 @@ void GLTFMetallic_Roughness::build_pipelines(VulkanEngine* engine) { build_transparent_pipeline(engine, meshVertexShader, meshFragShader, newLayout); - vkDestroyShaderModule(static_cast(engine->device), meshFragShader, nullptr); - vkDestroyShaderModule(static_cast(engine->device), meshVertexShader, nullptr); + vkDestroyShaderModule(engine->getLogicalDevice(), meshFragShader, nullptr); + vkDestroyShaderModule(engine->getLogicalDevice(), meshVertexShader, nullptr); } VkShaderModule GLTFMetallic_Roughness::load_shader(VulkanEngine* engine, const char* relative_path, const char* type) { VkShaderModule shaderModule; - if (!vkutil::load_shader_module(relative_path, static_cast(engine->device), + if (!vkutil::load_shader_module(relative_path, engine->getLogicalDevice(), &shaderModule)) { fmt::println("Error when building the {} shader module", type); } @@ -33,7 +33,7 @@ VkShaderModule GLTFMetallic_Roughness::load_shader(VulkanEngine* engine, } void GLTFMetallic_Roughness::create_material_layout(VulkanEngine* engine) { - materialLayout.create(static_cast(engine->device), + materialLayout.create(engine->getLogicalDevice(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, {{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER}, {1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}, @@ -57,7 +57,7 @@ VkPipelineLayout GLTFMetallic_Roughness::create_pipeline_layout( mesh_layout_info.pushConstantRangeCount = 1; VkPipelineLayout newLayout; - VK_CHECK(vkCreatePipelineLayout(static_cast(engine->device), &mesh_layout_info, nullptr, + VK_CHECK(vkCreatePipelineLayout(engine->getLogicalDevice(), &mesh_layout_info, nullptr, &newLayout)); return newLayout; @@ -79,7 +79,7 @@ void GLTFMetallic_Roughness::build_opaque_pipeline(VulkanEngine* engine, pipelineBuilder.set_depth_format(engine->_depthImage->get().imageFormat); pipelineBuilder._pipelineLayout = layout; - opaquePipeline.pipeline = pipelineBuilder.build_pipeline(static_cast(engine->device)); + opaquePipeline.pipeline = pipelineBuilder.build_pipeline(engine->getLogicalDevice()); } void GLTFMetallic_Roughness::build_transparent_pipeline( @@ -92,7 +92,7 @@ void GLTFMetallic_Roughness::build_transparent_pipeline( pipelineBuilder._pipelineLayout = layout; transparentPipeline.pipeline = - pipelineBuilder.build_pipeline(static_cast(engine->device)); + pipelineBuilder.build_pipeline(engine->getLogicalDevice()); } MaterialInstance GLTFMetallic_Roughness::write_material( diff --git a/src/graphics/vulkan/vk_command_buffers.cpp b/src/graphics/vulkan/vk_command_buffers.cpp index cee68e8e..be51f113 100644 --- a/src/graphics/vulkan/vk_command_buffers.cpp +++ b/src/graphics/vulkan/vk_command_buffers.cpp @@ -8,36 +8,36 @@ void CommandBuffers::init_commands(VulkanEngine* vk_engine) { // buffers const VkCommandPoolCreateInfo commandPoolInfo = vkinit::command_pool_create_info( - vk_engine->_graphicsQueueFamily, + vk_engine->getQueueIndex(), VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT); for (auto& _frame : vk_engine->command_buffers_container._frames) { VkCommandPool commandPool; - VK_CHECK(vkCreateCommandPool(static_cast(vk_engine->device), &commandPoolInfo, + VK_CHECK(vkCreateCommandPool(vk_engine->getLogicalDevice(), &commandPoolInfo, nullptr, &commandPool)); - _frame._commandPool = std::make_unique(static_cast(vk_engine->device), commandPool); + _frame._commandPool = std::make_unique(vk_engine->getLogicalDevice(), commandPool); // allocate the default command buffer that we will use for rendering VkCommandBufferAllocateInfo cmdAllocInfo = vkinit::command_buffer_allocate_info(_frame._commandPool->get(), 1); - VK_CHECK(vkAllocateCommandBuffers(static_cast(vk_engine->device), &cmdAllocInfo, + VK_CHECK(vkAllocateCommandBuffers(vk_engine->getLogicalDevice(), &cmdAllocInfo, &_frame._mainCommandBuffer)); } VkCommandPool immCommandPool; - VK_CHECK(vkCreateCommandPool(static_cast(vk_engine->device), &commandPoolInfo, nullptr, + VK_CHECK(vkCreateCommandPool(vk_engine->getLogicalDevice(), &commandPoolInfo, nullptr, &immCommandPool)); - vk_engine->command_buffers_container._immCommandPool = std::make_unique(static_cast(vk_engine->device), immCommandPool); + vk_engine->command_buffers_container._immCommandPool = std::make_unique(vk_engine->getLogicalDevice(), immCommandPool); // allocate the command buffer for immediate submits const VkCommandBufferAllocateInfo cmdAllocInfo = vkinit::command_buffer_allocate_info(vk_engine->command_buffers_container._immCommandPool->get(), 1); - VK_CHECK(vkAllocateCommandBuffers(static_cast(vk_engine->device), &cmdAllocInfo, + VK_CHECK(vkAllocateCommandBuffers(vk_engine->getLogicalDevice(), &cmdAllocInfo, &(vk_engine->command_buffers_container._immCommandBuffer))); // Smart pointer will automatically handle cleanup - no need for deletion queue @@ -46,7 +46,7 @@ void CommandBuffers::init_commands(VulkanEngine* vk_engine) { void CommandBuffers::immediate_submit( std::function&& function, VulkanEngine* vk_engine) const { - VK_CHECK(vkResetFences(static_cast(vk_engine->device), 1, vk_engine->command_buffers_container._immFence->getPtr())); + VK_CHECK(vkResetFences(vk_engine->getLogicalDevice(), 1, vk_engine->command_buffers_container._immFence->getPtr())); VK_CHECK(vkResetCommandBuffer(vk_engine->command_buffers_container._immCommandBuffer, 0)); const VkCommandBuffer cmd = vk_engine->command_buffers_container._immCommandBuffer; @@ -68,9 +68,9 @@ void CommandBuffers::immediate_submit( // submit command buffer to the queue and execute it. // _renderFence will now block until the graphic commands finish execution - VK_CHECK(vkQueueSubmit2(vk_engine->_graphicsQueue, 1, &submit, + VK_CHECK(vkQueueSubmit2(vk_engine->getQueue(), 1, &submit, vk_engine->command_buffers_container._immFence->get())); - VK_CHECK(vkWaitForFences(static_cast(vk_engine->device), 1, vk_engine->command_buffers_container._immFence->getPtr(), true, + VK_CHECK(vkWaitForFences(vk_engine->getLogicalDevice(), 1, vk_engine->command_buffers_container._immFence->getPtr(), true, 9999999999)); } diff --git a/src/graphics/vulkan/vk_command_buffers_container.cpp b/src/graphics/vulkan/vk_command_buffers_container.cpp index 85137f11..7f810870 100644 --- a/src/graphics/vulkan/vk_command_buffers_container.cpp +++ b/src/graphics/vulkan/vk_command_buffers_container.cpp @@ -15,18 +15,18 @@ void CommandBuffersContainer::init_sync_structures(VulkanEngine* vk_engine) { for (auto& _frame : _frames) { VkFence renderFence; - VK_CHECK(vkCreateFence(static_cast(vk_engine->device), &fenceCreateInfo, nullptr, &renderFence)); - _frame._renderFence = std::make_unique(static_cast(vk_engine->device), renderFence); + VK_CHECK(vkCreateFence(vk_engine->getLogicalDevice(), &fenceCreateInfo, nullptr, &renderFence)); + _frame._renderFence = std::make_unique(vk_engine->getLogicalDevice(), renderFence); VkSemaphore swapchainSemaphore, renderSemaphore; - VK_CHECK(vkCreateSemaphore(static_cast(vk_engine->device), &semaphoreCreateInfo, nullptr, &swapchainSemaphore)); - VK_CHECK(vkCreateSemaphore(static_cast(vk_engine->device), &semaphoreCreateInfo, nullptr, &renderSemaphore)); + VK_CHECK(vkCreateSemaphore(vk_engine->getLogicalDevice(), &semaphoreCreateInfo, nullptr, &swapchainSemaphore)); + VK_CHECK(vkCreateSemaphore(vk_engine->getLogicalDevice(), &semaphoreCreateInfo, nullptr, &renderSemaphore)); - _frame._swapchainSemaphore = std::make_unique(static_cast(vk_engine->device), swapchainSemaphore); - _frame._renderSemaphore = std::make_unique(static_cast(vk_engine->device), renderSemaphore); + _frame._swapchainSemaphore = std::make_unique(vk_engine->getLogicalDevice(), swapchainSemaphore); + _frame._renderSemaphore = std::make_unique(vk_engine->getLogicalDevice(), renderSemaphore); } VkFence immFence; - VK_CHECK(vkCreateFence(static_cast(vk_engine->device), &fenceCreateInfo, nullptr, &immFence)); - _immFence = std::make_unique(static_cast(vk_engine->device), immFence); + VK_CHECK(vkCreateFence(vk_engine->getLogicalDevice(), &fenceCreateInfo, nullptr, &immFence)); + _immFence = std::make_unique(vk_engine->getLogicalDevice(), immFence); } \ No newline at end of file diff --git a/src/graphics/vulkan/vk_engine.cpp b/src/graphics/vulkan/vk_engine.cpp index 50773489..19b49b37 100644 --- a/src/graphics/vulkan/vk_engine.cpp +++ b/src/graphics/vulkan/vk_engine.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -44,52 +45,6 @@ VulkanEngine& VulkanEngine::Get() { return *loadedEngine; } -#ifdef NDEBUG -constexpr bool bUseValidationLayers = false; -#else -constexpr bool bUseValidationLayers = true; -#endif - -void VulkanEngine::Instance::init() { - vkb::InstanceBuilder builder; - auto inst_ret = builder.set_app_name("TODO: PUT APP NAME HERE") - .set_engine_name("rainsystem") - .request_validation_layers(bUseValidationLayers) - .set_debug_callback(debugCallback) - .require_api_version(1, 3, 0) - .build(); - if (!inst_ret) { - LOGE("Failed to create Vulkan instance. Error: {}", - inst_ret.error().message()); - } - _instance = inst_ret.value(); -} - -VulkanEngine::Instance::~Instance() { - vkb::destroy_instance(_instance); -} - -VulkanEngine::Instance::operator VkInstance() const { return _instance; } -VulkanEngine::Instance::operator vkb::Instance() const { return _instance; } - -void VulkanEngine::Allocator::init(const VkPhysicalDevice& pGpu, - const VkDevice& pDevice, - const VkInstance& pInstance) { - const VmaAllocatorCreateInfo info = { - .flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT, - .physicalDevice = pGpu, - .device = pDevice, - .instance = pInstance - }; - vmaCreateAllocator(&info, &_allocator); -} - -VulkanEngine::Allocator::~Allocator() { - vmaDestroyAllocator(_allocator); -} - -VulkanEngine::Allocator::operator VmaAllocator() const { return _allocator; } - void VulkanEngine::Sampler::create(const VkDevice& pDevice, const VkSamplerCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator) { @@ -104,58 +59,9 @@ VulkanEngine::Sampler::~Sampler() { VulkanEngine::Sampler::operator VkSampler() const { return _sampler; } -VKAPI_ATTR VkBool32 VKAPI_CALL VulkanEngine::debugCallback( - VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - VkDebugUtilsMessageTypeFlagsEXT messageType, - const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, - [[maybe_unused]] void* pUserData) { - std::string type; - - switch (messageType) { - case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT: - type = "General"; - - break; - - case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT: - type = "Validation"; - - break; - - case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT: - type = "Performance"; - - break; - case VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT: - type = "Modified set of GPU-visible virtual addresses"; - break; - default: - type = "Unknown"; - } - - std::string message = "(" + type + ")" + pCallbackData->pMessage; - - if (messageSeverity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) { - LOGD(message) - } else if (messageSeverity == - VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) { - LOGI(message) - } else if (messageSeverity == - VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) { - LOGW(message) - } else if (messageSeverity >= - VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { - LOGE("{}", message) - } else { - LOGE("{}", message) - } - - return VK_FALSE; -} - void VulkanEngine::destroy_image(const AllocatedImage& img) const { - vkDestroyImageView(static_cast(device), img.imageView, nullptr); - vmaDestroyImage(static_cast(allocator), img.image, img.allocation); + vkDestroyImageView(info.getLogicalDevice(), img.imageView, nullptr); + vmaDestroyImage(info.getAllocator(), img.image, img.allocation); } void VulkanEngine::init_default_data() { @@ -184,17 +90,17 @@ void VulkanEngine::init_default_data() { const uint32_t white = glm::packUnorm4x8(glm::vec4(1, 1, 1, 1)); AllocatedImage whiteImageData = create_image(&white, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - _whiteImage = std::make_unique(static_cast(allocator), static_cast(device), whiteImageData); + _whiteImage = std::make_unique(info.getAllocator(), info.getLogicalDevice(), whiteImageData); const uint32_t grey = glm::packUnorm4x8(glm::vec4(0.66f, 0.66f, 0.66f, 1)); AllocatedImage greyImageData = create_image(&grey, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - _greyImage = std::make_unique(static_cast(allocator), static_cast(device), greyImageData); + _greyImage = std::make_unique(info.getAllocator(), info.getLogicalDevice(), greyImageData); const uint32_t black = glm::packUnorm4x8(glm::vec4(0, 0, 0, 0)); AllocatedImage blackImageData = create_image(&black, VkExtent3D{1, 1, 1}, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - _blackImage = std::make_unique(static_cast(allocator), static_cast(device), blackImageData); + _blackImage = std::make_unique(static_cast(info.getAllocator()), info.getLogicalDevice(), blackImageData); // checkerboard image const uint32_t magenta = glm::packUnorm4x8(glm::vec4(1, 0, 1, 1)); @@ -206,18 +112,18 @@ void VulkanEngine::init_default_data() { } AllocatedImage errorImageData = create_image(pixels.data(), VkExtent3D{16, 16, 1}, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT); - _errorCheckerboardImage = std::make_unique(static_cast(allocator), static_cast(device), errorImageData); + _errorCheckerboardImage = std::make_unique(info.getAllocator(), info.getLogicalDevice(), errorImageData); VkSamplerCreateInfo sampl = {.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; sampl.magFilter = VK_FILTER_NEAREST; sampl.minFilter = VK_FILTER_NEAREST; - _defaultSamplerNearest.create(static_cast(device), &sampl, nullptr); + _defaultSamplerNearest.create(info.getLogicalDevice(), &sampl, nullptr); sampl.magFilter = VK_FILTER_LINEAR; sampl.minFilter = VK_FILTER_LINEAR; - _defaultSamplerLinear.create(static_cast(device), &sampl, nullptr); + _defaultSamplerLinear.create(info.getLogicalDevice(), &sampl, nullptr); GLTFMetallic_Roughness::MaterialResources materialResources{}; // default the material textures @@ -239,7 +145,7 @@ void VulkanEngine::init_default_data() { sceneUniformData->metal_rough_factors = glm::vec4{1, 0.5, 0, 0}; // Store material constants buffer in managed buffers for automatic cleanup - _managedBuffers.push_back(std::make_unique(static_cast(allocator), materialConstants)); + _managedBuffers.push_back(std::make_unique(info.getAllocator(), materialConstants)); materialResources.dataBuffer = materialConstants.buffer; materialResources.dataBufferOffset = 0; @@ -318,23 +224,23 @@ void VulkanEngine::init_descriptors() { {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1}, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1}}; - globalDescriptorAllocator.init(static_cast(device), 10, sizes); + globalDescriptorAllocator.init(info.getLogicalDevice(), 10, sizes); // make the descriptor set layout for our compute draw - _drawImageDescriptorLayout.create(static_cast(device), VK_SHADER_STAGE_COMPUTE_BIT, {{0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE}}); - _gpuSceneDataDescriptorLayout.create(static_cast(device), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, {{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER}}); - _singleImageDescriptorLayout.create(static_cast(device), VK_SHADER_STAGE_FRAGMENT_BIT, {{0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}}); + _drawImageDescriptorLayout.create(info.getLogicalDevice(), VK_SHADER_STAGE_COMPUTE_BIT, {{0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE}}); + _gpuSceneDataDescriptorLayout.create(info.getLogicalDevice(), VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, {{0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER}}); + _singleImageDescriptorLayout.create(info.getLogicalDevice(), VK_SHADER_STAGE_FRAGMENT_BIT, {{0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}}); // allocate a descriptor set for our draw image _drawImageDescriptors = globalDescriptorAllocator.allocate( - static_cast(device), _drawImageDescriptorLayout.set); + info.getLogicalDevice(), _drawImageDescriptorLayout.set); DescriptorWriter writer; writer.write_image(0, _drawImage->imageView(), VK_NULL_HANDLE, VK_IMAGE_LAYOUT_GENERAL, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE); - writer.update_set(static_cast(device), _drawImageDescriptors); + writer.update_set(info.getLogicalDevice(), _drawImageDescriptors); for (auto& _frame : command_buffers_container._frames) { // create a descriptor pool @@ -346,14 +252,14 @@ void VulkanEngine::init_descriptors() { }; _frame._frameDescriptors = DescriptorAllocatorGrowable{}; - _frame._frameDescriptors.init(static_cast(device), 1000, frame_sizes); + _frame._frameDescriptors.init(info.getLogicalDevice(), 1000, frame_sizes); // No need for deletion queue - frame descriptors will be cleaned up in cleanup() } } void VulkanEngine::init_pipelines() { - pipelines.init(static_cast(device), _singleImageDescriptorLayout.set, _drawImageDescriptorLayout.set, _drawImage->get()); + pipelines.init(info.getLogicalDevice(), _singleImageDescriptorLayout.set, _drawImageDescriptorLayout.set, _drawImage->get()); // Pipeline cleanup is handled automatically by the Pipelines object metalRoughMaterial.build_pipelines(this); } @@ -362,7 +268,6 @@ VulkanEngine::VulkanEngine(Camera& camera) : mainCamera(&camera) { // only one engine initialization is allowed with the application. assert(loadedEngine == nullptr); loadedEngine = this; - init_vulkan(); init_swapchain(); command_buffers.init_commands(this); @@ -370,7 +275,7 @@ VulkanEngine::VulkanEngine(Camera& camera) : mainCamera(&camera) { command_buffers_container.init_sync_structures(this); init_descriptors(); init_pipelines(); - _imgui.init(static_cast(device), _window.ptr, static_cast(instance), _chosenGPU, _graphicsQueue, &_swapchainImageFormat); + _imgui.init(info.getLogicalDevice(), info.getWindow(), info.getInstance(), info.getPhysicalDevice(), info.getQueue(), &_swapchainImageFormat); init_default_data(); mainCamera->velocity = glm::vec3(0.f); @@ -389,89 +294,8 @@ VulkanEngine::VulkanEngine(Camera& camera) : mainCamera(&camera) { _isInitialized = true; } -void VulkanEngine::init_vulkan() { - auto system_info_ret = vkb::SystemInfo::get_system_info(); - if (!system_info_ret) { - LOGE("Failed to retrieve system info. Error: {}", - system_info_ret.error().message()); - } - - auto system_info = system_info_ret.value(); - - LOGI("Available layers:") - - for (auto& layer : system_info.available_layers) { - LOGI(layer.layerName); - } - - LOGI("Available extensions:") - - for (auto& [extensionName, _] : system_info.available_extensions) { - LOGI(extensionName); - } - // grab the instance - instance.init(); - - SDL_bool err = SDL_Vulkan_CreateSurface(_window.ptr, static_cast(instance), &_surface); - if (!err) { - LOGE("Failed to create Vulkan surface. Error: {}", SDL_GetError()); - } - - // vulkan 1.3 features - VkPhysicalDeviceVulkan13Features features{}; - features.dynamicRendering = true; - features.synchronization2 = true; - - // vulkan 1.2 features - VkPhysicalDeviceVulkan12Features features12{}; - features12.bufferDeviceAddress = true; - features12.descriptorIndexing = true; - - // use vkbootstrap to select a gpu. - // We want a gpu that can write to the SDL surface and supports vulkan 1.3 - // with the correct features - vkb::PhysicalDeviceSelector selector{static_cast(instance)}; - - auto physical_device_ret = selector.set_minimum_version(1, 3) - .set_required_features_13(features) - .set_required_features_12(features12) - .set_surface(_surface) - .select(); - - if (!physical_device_ret) { - LOGE("Failed to select physical device. Error: {}", - physical_device_ret.error().message()); - } - - const vkb::PhysicalDevice& physical_device = physical_device_ret.value(); - - // Get the VkDevice handle used in the rest of a vulkan application - device.init(physical_device); - _chosenGPU = physical_device.physical_device; - - auto queue_ret = static_cast(device).get_queue(vkb::QueueType::graphics); - if (!queue_ret) { - LOGE("Failed to retrieve graphics queue. Error: {}", - queue_ret.error().message()); - } - - _graphicsQueue = queue_ret.value(); - - auto queue_family_ret = static_cast(device).get_queue_index(vkb::QueueType::graphics); - if (!queue_family_ret) { - LOGE("Failed to retrieve graphics queue family. Error: {}", - queue_family_ret.error().message()); - } - - _graphicsQueueFamily = queue_family_ret.value(); - allocator.init(_chosenGPU, static_cast(device), static_cast(instance)); - - // VMA allocator will be destroyed in cleanup() - no need for deletion queue -} - - void VulkanEngine::create_swapchain(uint32_t width, uint32_t height) { - vkb::SwapchainBuilder swapchainBuilder{_chosenGPU, static_cast(device), _surface}; + vkb::SwapchainBuilder swapchainBuilder{info.getPhysicalDevice(), info.getLogicalDevice(), info.getSurface()}; _swapchainImageFormat = VK_FORMAT_B8G8R8A8_UNORM; @@ -531,7 +355,7 @@ void VulkanEngine::init_swapchain() { drawImageData.imageExtent = drawImageExtent; // allocate and create the image - vmaCreateImage(static_cast(allocator), &rimg_info, &rimg_allocinfo, &drawImageData.image, + vmaCreateImage(info.getAllocator(), &rimg_info, &rimg_allocinfo, &drawImageData.image, &drawImageData.allocation, nullptr); // build an image-view for the draw image to use for rendering @@ -539,11 +363,11 @@ void VulkanEngine::init_swapchain() { drawImageFormat, drawImageData.image, VK_IMAGE_ASPECT_COLOR_BIT); - VK_CHECK(vkCreateImageView(static_cast(device), &rview_info, nullptr, + VK_CHECK(vkCreateImageView(info.getLogicalDevice(), &rview_info, nullptr, &drawImageData.imageView)); // Create smart pointer for automatic cleanup - _drawImage = std::make_unique(static_cast(allocator), static_cast(device), drawImageData); + _drawImage = std::make_unique(info.getAllocator(), info.getLogicalDevice(), drawImageData); // Create depth image VkExtent3D depthImageExtent = { @@ -569,26 +393,26 @@ void VulkanEngine::init_swapchain() { depthImageData.imageExtent = depthImageExtent; // allocate and create the depth image - vmaCreateImage(static_cast(allocator), &dimg_info, &dimg_allocinfo, &depthImageData.image, + vmaCreateImage(info.getAllocator(), &dimg_info, &dimg_allocinfo, &depthImageData.image, &depthImageData.allocation, nullptr); // build an image-view for the depth image VkImageViewCreateInfo dview_info = vkinit::imageview_create_info( depthFormat, depthImageData.image, VK_IMAGE_ASPECT_DEPTH_BIT); - VK_CHECK(vkCreateImageView(static_cast(device), &dview_info, nullptr, + VK_CHECK(vkCreateImageView(info.getLogicalDevice(), &dview_info, nullptr, &depthImageData.imageView)); // Create smart pointer for automatic cleanup - _depthImage = std::make_unique(static_cast(allocator), static_cast(device), depthImageData); + _depthImage = std::make_unique(info.getAllocator(), info.getLogicalDevice(), depthImageData); } void VulkanEngine::destroy_swapchain() { - vkDestroySwapchainKHR(static_cast(device), _swapchain, nullptr); + vkDestroySwapchainKHR(info.getLogicalDevice(), _swapchain, nullptr); // destroy swapchain resources for (const auto& _swapchainImageView : _swapchainImageViews) { - vkDestroyImageView(static_cast(device), _swapchainImageView, nullptr); + vkDestroyImageView(info.getLogicalDevice(), _swapchainImageView, nullptr); } } @@ -596,7 +420,7 @@ void VulkanEngine::destroy_swapchain() { { if (_isInitialized) { // make sure the gpu has stopped doing its things - vkDeviceWaitIdle(static_cast(device)); + vkDeviceWaitIdle(info.getLogicalDevice()); loadedScenes.clear(); @@ -604,13 +428,11 @@ void VulkanEngine::destroy_swapchain() { for (auto& _frame : command_buffers_container._frames) { // Destroy frame descriptors manually - _frame._frameDescriptors.destroy_pools(static_cast(device)); + _frame._frameDescriptors.destroy_pools(info.getLogicalDevice()); } - globalDescriptorAllocator.destroy_pools(static_cast(device)); + globalDescriptorAllocator.destroy_pools(info.getLogicalDevice()); destroy_swapchain(); - - vkDestroySurfaceKHR(static_cast(instance), _surface, nullptr); } // clear engine pointer @@ -634,7 +456,7 @@ AllocatedBuffer VulkanEngine::create_buffer(size_t allocSize, AllocatedBuffer newBuffer{}; // allocate the buffer - VK_CHECK(vmaCreateBuffer(static_cast(allocator), &bufferInfo, &vmallocinfo, + VK_CHECK(vmaCreateBuffer(info.getAllocator(), &bufferInfo, &vmallocinfo, &newBuffer.buffer, &newBuffer.allocation, &newBuffer.info)); @@ -642,7 +464,7 @@ AllocatedBuffer VulkanEngine::create_buffer(size_t allocSize, } void VulkanEngine::destroy_buffer(const AllocatedBuffer& buffer) const { - vmaDestroyBuffer(static_cast(allocator), buffer.buffer, buffer.allocation); + vmaDestroyBuffer(info.getAllocator(), buffer.buffer, buffer.allocation); } GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, @@ -665,7 +487,7 @@ GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, .buffer = newSurface.vertexBuffer.buffer}; newSurface.vertexBufferAddress = - vkGetBufferDeviceAddress(static_cast(device), &deviceAddressInfo); + vkGetBufferDeviceAddress(info.getLogicalDevice(), &deviceAddressInfo); // create index buffer newSurface.indexBuffer = create_buffer( @@ -704,8 +526,8 @@ GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, this); // Store mesh buffers in managed collections for automatic cleanup - _managedBuffers.push_back(std::make_unique(static_cast(allocator), newSurface.vertexBuffer)); - _managedBuffers.push_back(std::make_unique(static_cast(allocator), newSurface.indexBuffer)); + _managedBuffers.push_back(std::make_unique(info.getAllocator(), newSurface.vertexBuffer)); + _managedBuffers.push_back(std::make_unique(info.getAllocator(), newSurface.indexBuffer)); destroy_buffer(staging); return newSurface; @@ -748,7 +570,7 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { // add it to the current frame's managed buffers for automatic cleanup get_current_frame()._frameBuffers.push_back( - std::make_unique(static_cast(allocator), gpuSceneDataBuffer)); + std::make_unique(info.getAllocator(), gpuSceneDataBuffer)); // write the buffer auto* sceneUniformData = @@ -758,12 +580,12 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { // create a descriptor set that binds that buffer and update it VkDescriptorSet globalDescriptor = get_current_frame()._frameDescriptors.allocate( - static_cast(device), _gpuSceneDataDescriptorLayout.set); + info.getLogicalDevice(), _gpuSceneDataDescriptorLayout.set); DescriptorWriter writer; writer.write_buffer(0, gpuSceneDataBuffer.buffer, sizeof(GPUSceneData), 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); - writer.update_set(static_cast(device), globalDescriptor); + writer.update_set(info.getLogicalDevice(), globalDescriptor); VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info( _drawImage->imageView(), nullptr, VK_IMAGE_LAYOUT_GENERAL); @@ -795,13 +617,13 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { // bind a texture VkDescriptorSet imageSet = get_current_frame()._frameDescriptors.allocate( - static_cast(device), _singleImageDescriptorLayout.set); + info.getLogicalDevice(), _singleImageDescriptorLayout.set); DescriptorWriter single_image_writer; single_image_writer.write_image(0, _errorCheckerboardImage->imageView(), static_cast(_defaultSamplerNearest), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); - single_image_writer.update_set(static_cast(device), imageSet); + single_image_writer.update_set(info.getLogicalDevice(), imageSet); pipelines.meshPipeline->bindDescriptorSets(cmd, &imageSet, 1); @@ -836,19 +658,19 @@ void VulkanEngine::draw() { // wait until the gpu has finished rendering the last frame. Timeout of 1 // second - VK_CHECK(vkWaitForFences(static_cast(device), 1, get_current_frame()._renderFence->getPtr(), + VK_CHECK(vkWaitForFences(info.getLogicalDevice(), 1, get_current_frame()._renderFence->getPtr(), true, 1000000000)); // Clear frame buffers instead of flushing deletion queue get_current_frame()._frameBuffers.clear(); - get_current_frame()._frameDescriptors.clear_pools(static_cast(device)); + get_current_frame()._frameDescriptors.clear_pools(info.getLogicalDevice()); - VK_CHECK(vkResetFences(static_cast(device), 1, get_current_frame()._renderFence->getPtr())); + VK_CHECK(vkResetFences(info.getLogicalDevice(), 1, get_current_frame()._renderFence->getPtr())); // request image from the swapchain uint32_t swapchainImageIndex; const VkResult e = - vkAcquireNextImageKHR(static_cast(device), _swapchain, 1000000000, + vkAcquireNextImageKHR(info.getLogicalDevice(), _swapchain, 1000000000, get_current_frame()._swapchainSemaphore->get(), nullptr, &swapchainImageIndex); if (e == VK_ERROR_OUT_OF_DATE_KHR) { @@ -944,7 +766,7 @@ void VulkanEngine::draw() { // submit command buffer to the queue and execute it. // _renderFence will now block until the graphic commands finish execution - VK_CHECK(vkQueueSubmit2(_graphicsQueue, 1, &submit, + VK_CHECK(vkQueueSubmit2(info.getQueue(), 1, &submit, get_current_frame()._renderFence->get())); // prepare present @@ -963,7 +785,7 @@ void VulkanEngine::draw() { presentInfo.pImageIndices = &swapchainImageIndex; - VkResult presentResult = vkQueuePresentKHR(_graphicsQueue, &presentInfo); + VkResult presentResult = vkQueuePresentKHR(info.getQueue(), &presentInfo); if (presentResult == VK_ERROR_OUT_OF_DATE_KHR) { resize_requested = true; } @@ -973,12 +795,12 @@ void VulkanEngine::draw() { } void VulkanEngine::resize_swapchain() { - vkDeviceWaitIdle(static_cast(device)); + vkDeviceWaitIdle(info.getLogicalDevice()); destroy_swapchain(); int w, h; - SDL_GetWindowSize(_window.ptr, &w, &h); + SDL_GetWindowSize(info.getWindow(), &w, &h); _windowExtent.width = static_cast(w); _windowExtent.height = static_cast(h); @@ -1016,7 +838,7 @@ AllocatedImage VulkanEngine::create_image(VkExtent3D size, VkFormat format, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); // allocate and create the image - VK_CHECK(vmaCreateImage(static_cast(allocator), &img_info, &allocinfo, &newImage.image, + VK_CHECK(vmaCreateImage(info.getAllocator(), &img_info, &allocinfo, &newImage.image, &newImage.allocation, nullptr)); // if the format is a depth format, we will need to have it use the correct @@ -1031,7 +853,7 @@ AllocatedImage VulkanEngine::create_image(VkExtent3D size, VkFormat format, vkinit::imageview_create_info(format, newImage.image, aspectFlag); view_info.subresourceRange.levelCount = img_info.mipLevels; - VK_CHECK(vkCreateImageView(static_cast(device), &view_info, nullptr, + VK_CHECK(vkCreateImageView(info.getLogicalDevice(), &view_info, nullptr, &newImage.imageView)); return newImage; diff --git a/src/graphics/vulkan/vk_loader.cpp b/src/graphics/vulkan/vk_loader.cpp index 39f70274..643a0ee4 100644 --- a/src/graphics/vulkan/vk_loader.cpp +++ b/src/graphics/vulkan/vk_loader.cpp @@ -251,7 +251,7 @@ std::optional> loadGltf(VulkanEngine* engine, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 3}, {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1}}; file.descriptorPool.init( - static_cast(engine->device), + engine->getLogicalDevice(), static_cast(std::max(gltf.materials.size(), size_t(1))), sizes); @@ -269,7 +269,7 @@ std::optional> loadGltf(VulkanEngine* engine, sampl.mipmapMode = extract_mipmap_mode( sampler.minFilter.value_or(fastgltf::Filter::Nearest)); VkSampler newSampler; - vkCreateSampler(static_cast(engine->device), &sampl, nullptr, &newSampler); + vkCreateSampler(engine->getLogicalDevice(), &sampl, nullptr, &newSampler); file.samplers.push_back(newSampler); } @@ -524,7 +524,7 @@ void LoadedGLTF::Draw(const glm::mat4& topMatrix, DrawContext& ctx) { } void LoadedGLTF::clearAll() { - VkDevice dv = static_cast(creator->device); + VkDevice dv = creator->getLogicalDevice(); descriptorPool.destroy_pools(dv); creator->destroy_buffer(materialDataBuffer); // for (auto& [k, v] : meshes) { diff --git a/src/include/graphics/vulkan/Device.h b/src/include/graphics/vulkan/Device.h index 4ae8eebe..c30e6ae2 100644 --- a/src/include/graphics/vulkan/Device.h +++ b/src/include/graphics/vulkan/Device.h @@ -3,11 +3,8 @@ #include struct Device { - explicit operator VkDevice() const; - explicit operator vkb::Device() const; - void init(const vkb::PhysicalDevice& physical_device); + VkPhysicalDevice physical; + vkb::Device logical; + Device(const vkb::Instance &instance, VkSurfaceKHR surface); ~Device(); - -private: - vkb::Device _device; }; diff --git a/src/include/graphics/vulkan/VulkanInit.h b/src/include/graphics/vulkan/VulkanInit.h new file mode 100644 index 00000000..bac92fcc --- /dev/null +++ b/src/include/graphics/vulkan/VulkanInit.h @@ -0,0 +1,64 @@ +#pragma once +#include +#include +#include + +#include "Device.h" + +struct VulkanInit { + VulkanInit(); + VkDevice getLogicalDevice() const { return device.logical; } + VkPhysicalDevice getPhysicalDevice() const { return device.physical; } + VkQueue getQueue() const { return queue.queue; } + uint32_t getQueueIndex() const { return queue.index; } + VmaAllocator getAllocator() const { return allocator.allocator; } + VkInstance getInstance() const { return instance.instance; } + VkSurfaceKHR getSurface() const { return surface.surface; } + SDL_Window* getWindow() const { return window.window; } +private: + struct Instance { + vkb::Instance instance; + Instance(); + ~Instance(); + }; + Instance instance; + + struct Window { + SDL_Window* window; + Window(); + ~Window(); + }; + Window window; + + struct Surface { + VkSurfaceKHR surface{VK_NULL_HANDLE}; // Vulkan window surface + explicit Surface(SDL_Window *window, const VkInstance& instance); + ~Surface(); + private: + VkInstance _instance{VK_NULL_HANDLE}; + }; + Surface surface; + + Device device; + + struct Queue { + VkQueue queue; + uint32_t index; + operator VkQueue() const { return queue; } + Queue(const vkb::Device& dv); + }; + Queue queue; + + struct Allocator { + VmaAllocator allocator{VK_NULL_HANDLE}; + Allocator(const VkPhysicalDevice& gpu, const VkDevice& device, const VkInstance& instance); + ~Allocator(); + }; + Allocator allocator; + + static VKAPI_ATTR VkBool32 VKAPI_CALL + debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData); +}; diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 9434e60e..aefa9242 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,7 @@ #include "ComputePipeline.h" #include "DescriptorSetLayout.h" #include "Device.h" +#include "VulkanInit.h" #include "core/ModelImpl.h" #include "pipelines.h" #include "vk_command_buffers.h" @@ -66,26 +68,11 @@ struct DrawContext { }; class VulkanEngine { + VulkanInit info; public: - struct Instance { - explicit operator VkInstance() const; - explicit operator vkb::Instance() const; - void init(); - ~Instance(); - private: - vkb::Instance _instance; - }; - Instance instance; - Device device; - - struct Allocator { - explicit operator VmaAllocator() const; - void init(const VkPhysicalDevice& gpu, const VkDevice& device, const VkInstance& instance); - ~Allocator(); - private: - VmaAllocator _allocator{VK_NULL_HANDLE}; - }; - Allocator allocator; + VkDevice getLogicalDevice() const { return info.getLogicalDevice(); } + VkQueue getQueue() const { return info.getQueue(); } + uint32_t getQueueIndex() const { return info.getQueueIndex(); } Pipelines pipelines; @@ -113,9 +100,6 @@ class VulkanEngine { return command_buffers_container.get_current_frame(_frameNumber); }; - VkQueue _graphicsQueue; - uint32_t _graphicsQueueFamily; - bool _isInitialized{false}; unsigned int _frameNumber{0}; bool stop_rendering{false}; @@ -131,8 +115,6 @@ class VulkanEngine { // run main loop void update(); - VkPhysicalDevice _chosenGPU; // GPU chosen as the default device - VkSurfaceKHR _surface; // Vulkan window surface VkSwapchainKHR _swapchain; VkFormat _swapchainImageFormat; @@ -203,13 +185,6 @@ class VulkanEngine { // initializes everything in the engine VulkanEngine(Camera& camera); - struct Window { - SDL_Window* ptr; - Window() : ptr{SDL_CreateWindow("engine", SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, 1700, 900, SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE)} {} - ~Window() { SDL_DestroyWindow(ptr); } - }; - Window _window; ~VulkanEngine(); void destroy_buffer(const AllocatedBuffer& buffer) const; private: @@ -228,13 +203,6 @@ class VulkanEngine { std::vector> _managedBuffers; std::vector> _managedImages; - static VKAPI_ATTR VkBool32 VKAPI_CALL - debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, - VkDebugUtilsMessageTypeFlagsEXT messageType, - const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, - void* pUserData); - - void init_vulkan(); void init_swapchain(); void create_swapchain(uint32_t width, uint32_t height);