diff --git a/demo/main.cpp b/demo/main.cpp index 4585d801..5ebc9c0a 100644 --- a/demo/main.cpp +++ b/demo/main.cpp @@ -3,11 +3,12 @@ #include #include "IController.h" +#include "core/Controller.h" int main([[maybe_unused]] int argc, [[maybe_unused]] char* args[]) { try { - const auto controller = createInstance(); - controller->init(); + const auto controller = createController(); + 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/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/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 f8e28caa..1258b1f7 100644 --- a/src/core/ModelImpl.cpp +++ b/src/core/ModelImpl.cpp @@ -1,22 +1,12 @@ #include "core/ModelImpl.h" - -#include - #include "core/Mesh.h" +#include "graphics/vulkan/vk_engine.h" -ModelImpl::~ModelImpl() { - _engine.cleanup(); -} - -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 deleted file mode 100644 index fe1eb2bb..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 model = createModel(); - const auto controller = createController(model); - return controller; -} diff --git a/src/graphics/CMakeLists.txt b/src/graphics/CMakeLists.txt index e117fd76..32a4feb1 100644 --- a/src/graphics/CMakeLists.txt +++ b/src/graphics/CMakeLists.txt @@ -10,6 +10,9 @@ target_sources(${PROJECT_NAME} vulkan/vk_pipelines.cpp vulkan/pipelines.cpp vulkan/ComputePipeline.cpp + 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/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/Device.cpp b/src/graphics/vulkan/Device.cpp new file mode 100644 index 00000000..8349f442 --- /dev/null +++ b/src/graphics/vulkan/Device.cpp @@ -0,0 +1,46 @@ +#include "graphics/vulkan/Device.h" + +#include "core/Logging.h" + +Device::Device(const vkb::Instance& instance, const VkSurfaceKHR surface) + : physical(VK_NULL_HANDLE), logical([&] { + VkPhysicalDeviceVulkan13Features features{ + .synchronization2 = true, + .dynamicRendering = true, + }; + + VkPhysicalDeviceVulkan12Features features12{ + .descriptorIndexing = true, + .bufferDeviceAddress = 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{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()); + } + + 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 5926226b..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 = 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(engine->_device, meshFragShader, nullptr); - vkDestroyShaderModule(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, engine->_device, + if (!vkutil::load_shader_module(relative_path, engine->getLogicalDevice(), &shaderModule)) { fmt::println("Error when building the {} shader module", type); } @@ -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->_device, - VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT); + 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}, + {2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}}); } VkPipelineLayout GLTFMetallic_Roughness::create_pipeline_layout( @@ -50,8 +47,8 @@ VkPipelineLayout GLTFMetallic_Roughness::create_pipeline_layout( matrixRange.size = sizeof(GPUDrawPushConstants); matrixRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; - VkDescriptorSetLayout layouts[] = {engine->_gpuSceneDataDescriptorLayout, - materialLayout}; + VkDescriptorSetLayout layouts[] = {engine->_gpuSceneDataDescriptorLayout.set, + materialLayout.set}; VkPipelineLayoutCreateInfo mesh_layout_info = vkinit::pipeline_layout_create_info(); mesh_layout_info.setLayoutCount = 2; @@ -60,7 +57,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->getLogicalDevice(), &mesh_layout_info, nullptr, &newLayout)); return newLayout; @@ -82,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->_device); + opaquePipeline.pipeline = pipelineBuilder.build_pipeline(engine->getLogicalDevice()); } void GLTFMetallic_Roughness::build_transparent_pipeline( @@ -95,18 +92,18 @@ void GLTFMetallic_Roughness::build_transparent_pipeline( pipelineBuilder._pipelineLayout = layout; transparentPipeline.pipeline = - pipelineBuilder.build_pipeline(engine->_device); + pipelineBuilder.build_pipeline(engine->getLogicalDevice()); } MaterialInstance GLTFMetallic_Roughness::write_material( - VkDevice device, MaterialPass pass, const MaterialResources& resources, + MaterialPass pass, const MaterialResources& resources, DescriptorAllocatorGrowable& descriptorAllocator) { MaterialInstance matData{}; matData.passType = pass; 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_command_buffers.cpp b/src/graphics/vulkan/vk_command_buffers.cpp index 9114b9ed..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(vk_engine->_device, &commandPoolInfo, + VK_CHECK(vkCreateCommandPool(vk_engine->getLogicalDevice(), &commandPoolInfo, nullptr, &commandPool)); - _frame._commandPool = std::make_unique(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(vk_engine->_device, &cmdAllocInfo, + VK_CHECK(vkAllocateCommandBuffers(vk_engine->getLogicalDevice(), &cmdAllocInfo, &_frame._mainCommandBuffer)); } VkCommandPool immCommandPool; - VK_CHECK(vkCreateCommandPool(vk_engine->_device, &commandPoolInfo, nullptr, + VK_CHECK(vkCreateCommandPool(vk_engine->getLogicalDevice(), &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->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(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(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(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 c1abcc6c..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(vk_engine->_device, &fenceCreateInfo, nullptr, &renderFence)); - _frame._renderFence = std::make_unique(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(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->getLogicalDevice(), &semaphoreCreateInfo, nullptr, &swapchainSemaphore)); + VK_CHECK(vkCreateSemaphore(vk_engine->getLogicalDevice(), &semaphoreCreateInfo, nullptr, &renderSemaphore)); + + _frame._swapchainSemaphore = std::make_unique(vk_engine->getLogicalDevice(), swapchainSemaphore); + _frame._renderSemaphore = std::make_unique(vk_engine->getLogicalDevice(), 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->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 99b94292..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 @@ -17,8 +18,10 @@ #include "core/Logging.h" #include "core/config.h" #include "graphics/vulkan/vk_descriptors.h" +#include "graphics/vulkan/DescriptorSetLayout.h" #include "scene/Camera.h" + #define VMA_IMPLEMENTATION #include #include @@ -35,7 +38,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; @@ -43,59 +45,23 @@ VulkanEngine& VulkanEngine::Get() { return *loadedEngine; } -#ifdef NDEBUG -constexpr bool bUseValidationLayers = false; -#else -constexpr bool bUseValidationLayers = true; -#endif - -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"; +void VulkanEngine::Sampler::create(const VkDevice& pDevice, + const VkSamplerCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator) { + _device = pDevice; + _allocator = pAllocator; + vkCreateSampler(_device, pCreateInfo, pAllocator, &_sampler); +} - break; - case VK_DEBUG_UTILS_MESSAGE_TYPE_DEVICE_ADDRESS_BINDING_BIT_EXT: - type = "Modified set of GPU-visible virtual addresses"; - break; - default: - type = "Unknown"; - } +VulkanEngine::Sampler::~Sampler() { + vkDestroySampler(_device, _sampler, _allocator); +} - 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) - } +VulkanEngine::Sampler::operator VkSampler() const { return _sampler; } - return VK_FALSE; +void VulkanEngine::destroy_image(const AllocatedImage& img) const { + vkDestroyImageView(info.getLogicalDevice(), img.imageView, nullptr); + vmaDestroyImage(info.getAllocator(), img.image, img.allocation); } void VulkanEngine::init_default_data() { @@ -124,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(_allocator, _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(_allocator, _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(_allocator, _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)); @@ -146,26 +112,25 @@ 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(info.getAllocator(), info.getLogicalDevice(), errorImageData); VkSamplerCreateInfo sampl = {.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; sampl.magFilter = VK_FILTER_NEAREST; sampl.minFilter = VK_FILTER_NEAREST; - - vkCreateSampler(_device, &sampl, nullptr, &_defaultSamplerNearest); + _defaultSamplerNearest.create(info.getLogicalDevice(), &sampl, nullptr); sampl.magFilter = VK_FILTER_LINEAR; sampl.minFilter = VK_FILTER_LINEAR; - vkCreateSampler(_device, &sampl, nullptr, &_defaultSamplerLinear); + _defaultSamplerLinear.create(info.getLogicalDevice(), &sampl, nullptr); GLTFMetallic_Roughness::MaterialResources materialResources{}; // default the material textures materialResources.colorImage = _whiteImage->get(); - materialResources.colorSampler = _defaultSamplerLinear; + materialResources.colorSampler = static_cast(_defaultSamplerLinear); materialResources.metalRoughImage = _whiteImage->get(); - materialResources.metalRoughSampler = _defaultSamplerLinear; + materialResources.metalRoughSampler = static_cast(_defaultSamplerLinear); // set the uniform buffer for the material data const AllocatedBuffer materialConstants = create_buffer( @@ -180,78 +145,77 @@ 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(info.getAllocator(), materialConstants)); materialResources.dataBuffer = materialConstants.buffer; materialResources.dataBufferOffset = 0; defaultData = metalRoughMaterial.write_material( - _device, MaterialPass::MainColor, materialResources, + MaterialPass::MainColor, materialResources, globalDescriptorAllocator); } -void VulkanEngine::init_imgui() { + 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. 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)); +} + void VulkanEngine::Imgui::init(const VkDevice& dev, SDL_Window* w, + const VkInstance& pInstance, 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(_window); - + ImGui_ImplSDL2_InitForVulkan(w); // 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 = pInstance, + .PhysicalDevice = physicalDevice, + .Device = _device, + .Queue = queue, + .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; + format; 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() { + ImGui_ImplVulkan_Shutdown(); + vkDestroyDescriptorPool(_device, _imguiPool, nullptr); } void VulkanEngine::init_descriptors() { @@ -260,41 +224,23 @@ void VulkanEngine::init_descriptors() { {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1}, {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1}}; - globalDescriptorAllocator.init(_device, 10, sizes); + globalDescriptorAllocator.init(info.getLogicalDevice(), 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); - } - - { - DescriptorLayoutBuilder builder; - builder.add_binding(0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); - _gpuSceneDataDescriptorLayout = - builder.build(_device, 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(_device, VK_SHADER_STAGE_FRAGMENT_BIT); - } + _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( - _device, _drawImageDescriptorLayout); + 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(_device, _drawImageDescriptors); + writer.update_set(info.getLogicalDevice(), _drawImageDescriptors); for (auto& _frame : command_buffers_container._frames) { // create a descriptor pool @@ -306,33 +252,30 @@ void VulkanEngine::init_descriptors() { }; _frame._frameDescriptors = DescriptorAllocatorGrowable{}; - _frame._frameDescriptors.init(_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(_device, _singleImageDescriptorLayout, _drawImageDescriptorLayout, _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); } -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(info.getLogicalDevice(), info.getWindow(), info.getInstance(), info.getPhysicalDevice(), info.getQueue(), &_swapchainImageFormat); init_default_data(); mainCamera->velocity = glm::vec3(0.f); @@ -351,124 +294,8 @@ void VulkanEngine::init(SDL_Window* window) { _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); - } - - 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 = vkb_inst.instance; - _debug_messenger = vkb_inst.debug_messenger; - - SDL_bool err = SDL_Vulkan_CreateSurface(_window, _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{vkb_inst}; - - 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& 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(); - - // Get the VkDevice handle used in the rest of a vulkan application - _device = vkbDevice.device; - _chosenGPU = physicalDevice.physical_device; - - auto queue_ret = vkbDevice.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 = vkbDevice.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(); - - // initialize the memory allocator - VmaAllocatorCreateInfo allocatorInfo = {}; - allocatorInfo.physicalDevice = _chosenGPU; - allocatorInfo.device = _device; - allocatorInfo.instance = _instance; - allocatorInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; - vmaCreateAllocator(&allocatorInfo, &_allocator); - - // 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{info.getPhysicalDevice(), info.getLogicalDevice(), info.getSurface()}; _swapchainImageFormat = VK_FORMAT_B8G8R8A8_UNORM; @@ -528,7 +355,7 @@ void VulkanEngine::init_swapchain() { drawImageData.imageExtent = drawImageExtent; // allocate and create the image - vmaCreateImage(_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 @@ -536,11 +363,11 @@ void VulkanEngine::init_swapchain() { drawImageFormat, drawImageData.image, VK_IMAGE_ASPECT_COLOR_BIT); - VK_CHECK(vkCreateImageView(_device, &rview_info, nullptr, + VK_CHECK(vkCreateImageView(info.getLogicalDevice(), &rview_info, nullptr, &drawImageData.imageView)); // Create smart pointer for automatic cleanup - _drawImage = std::make_unique(_allocator, _device, drawImageData); + _drawImage = std::make_unique(info.getAllocator(), info.getLogicalDevice(), drawImageData); // Create depth image VkExtent3D depthImageExtent = { @@ -566,59 +393,46 @@ void VulkanEngine::init_swapchain() { depthImageData.imageExtent = depthImageExtent; // allocate and create the depth image - vmaCreateImage(_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(_device, &dview_info, nullptr, + VK_CHECK(vkCreateImageView(info.getLogicalDevice(), &dview_info, nullptr, &depthImageData.imageView)); // Create smart pointer for automatic cleanup - _depthImage = std::make_unique(_allocator, _device, depthImageData); + _depthImage = std::make_unique(info.getAllocator(), info.getLogicalDevice(), depthImageData); } void VulkanEngine::destroy_swapchain() { - vkDestroySwapchainKHR(_device, _swapchain, nullptr); + vkDestroySwapchainKHR(info.getLogicalDevice(), _swapchain, nullptr); // destroy swapchain resources for (const auto& _swapchainImageView : _swapchainImageViews) { - vkDestroyImageView(_device, _swapchainImageView, nullptr); + vkDestroyImageView(info.getLogicalDevice(), _swapchainImageView, nullptr); } } -void VulkanEngine::cleanup() { + VulkanEngine::~VulkanEngine() + { if (_isInitialized) { // make sure the gpu has stopped doing its things - vkDeviceWaitIdle(_device); + vkDeviceWaitIdle(info.getLogicalDevice()); loadedScenes.clear(); // 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(_device, _frame._commandPool->get(), nullptr); - } - // Destroy frame descriptors manually - _frame._frameDescriptors.destroy_pools(_device); + _frame._frameDescriptors.destroy_pools(info.getLogicalDevice()); } + globalDescriptorAllocator.destroy_pools(info.getLogicalDevice()); 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); } // clear engine pointer @@ -642,7 +456,7 @@ AllocatedBuffer VulkanEngine::create_buffer(size_t allocSize, AllocatedBuffer newBuffer{}; // allocate the buffer - VK_CHECK(vmaCreateBuffer(_allocator, &bufferInfo, &vmallocinfo, + VK_CHECK(vmaCreateBuffer(info.getAllocator(), &bufferInfo, &vmallocinfo, &newBuffer.buffer, &newBuffer.allocation, &newBuffer.info)); @@ -650,7 +464,7 @@ AllocatedBuffer VulkanEngine::create_buffer(size_t allocSize, } void VulkanEngine::destroy_buffer(const AllocatedBuffer& buffer) const { - vmaDestroyBuffer(_allocator, buffer.buffer, buffer.allocation); + vmaDestroyBuffer(info.getAllocator(), buffer.buffer, buffer.allocation); } GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, @@ -673,7 +487,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(info.getLogicalDevice(), &deviceAddressInfo); // create index buffer newSurface.indexBuffer = create_buffer( @@ -712,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(_allocator, newSurface.vertexBuffer)); - _managedBuffers.push_back(std::make_unique(_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; @@ -756,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(_allocator, gpuSceneDataBuffer)); + std::make_unique(info.getAllocator(), gpuSceneDataBuffer)); // write the buffer auto* sceneUniformData = @@ -766,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( - _device, _gpuSceneDataDescriptorLayout); + info.getLogicalDevice(), _gpuSceneDataDescriptorLayout.set); DescriptorWriter writer; writer.write_buffer(0, gpuSceneDataBuffer.buffer, sizeof(GPUSceneData), 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER); - writer.update_set(_device, globalDescriptor); + writer.update_set(info.getLogicalDevice(), globalDescriptor); VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info( _drawImage->imageView(), nullptr, VK_IMAGE_LAYOUT_GENERAL); @@ -803,13 +617,13 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) { // bind a texture VkDescriptorSet imageSet = get_current_frame()._frameDescriptors.allocate( - _device, _singleImageDescriptorLayout); + info.getLogicalDevice(), _singleImageDescriptorLayout.set); DescriptorWriter single_image_writer; single_image_writer.write_image(0, _errorCheckerboardImage->imageView(), - _defaultSamplerNearest, + static_cast(_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(info.getLogicalDevice(), imageSet); pipelines.meshPipeline->bindDescriptorSets(cmd, &imageSet, 1); @@ -844,19 +658,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(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(_device); + get_current_frame()._frameDescriptors.clear_pools(info.getLogicalDevice()); - VK_CHECK(vkResetFences(_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(_device, _swapchain, 1000000000, + vkAcquireNextImageKHR(info.getLogicalDevice(), _swapchain, 1000000000, get_current_frame()._swapchainSemaphore->get(), nullptr, &swapchainImageIndex); if (e == VK_ERROR_OUT_OF_DATE_KHR) { @@ -952,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 @@ -971,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; } @@ -981,12 +795,12 @@ void VulkanEngine::draw() { } void VulkanEngine::resize_swapchain() { - vkDeviceWaitIdle(_device); + vkDeviceWaitIdle(info.getLogicalDevice()); destroy_swapchain(); int w, h; - SDL_GetWindowSize(_window, &w, &h); + SDL_GetWindowSize(info.getWindow(), &w, &h); _windowExtent.width = static_cast(w); _windowExtent.height = static_cast(h); @@ -1024,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(_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 @@ -1039,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(_device, &view_info, nullptr, + VK_CHECK(vkCreateImageView(info.getLogicalDevice(), &view_info, nullptr, &newImage.imageView)); return newImage; @@ -1094,10 +908,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; @@ -1171,7 +981,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..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( - 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(engine->_device, &sampl, nullptr, &newSampler); + vkCreateSampler(engine->getLogicalDevice(), &sampl, nullptr, &newSampler); file.samplers.push_back(newSampler); } @@ -319,9 +319,9 @@ std::optional> loadGltf(VulkanEngine* engine, GLTFMetallic_Roughness::MaterialResources materialResources; materialResources.colorImage = engine->_whiteImage->get(); - materialResources.colorSampler = engine->_defaultSamplerLinear; + materialResources.colorSampler = static_cast(engine->_defaultSamplerLinear); materialResources.metalRoughImage = engine->_whiteImage->get(); - materialResources.metalRoughSampler = engine->_defaultSamplerLinear; + materialResources.metalRoughSampler = static_cast(engine->_defaultSamplerLinear); materialResources.dataBuffer = file.materialDataBuffer.buffer; materialResources.dataBufferOffset = data_index * sizeof(GLTFMetallic_Roughness::MaterialConstants); @@ -338,7 +338,7 @@ std::optional> loadGltf(VulkanEngine* engine, } newMat->data = engine->metalRoughMaterial.write_material( - engine->_device, passType, materialResources, + passType, materialResources, file.descriptorPool); data_index++; } @@ -356,14 +356,14 @@ std::optional> loadGltf(VulkanEngine* engine, GLTFMetallic_Roughness::MaterialResources resources; resources.colorImage = engine->_whiteImage->get(); - resources.colorSampler = engine->_defaultSamplerLinear; + resources.colorSampler = static_cast(engine->_defaultSamplerLinear); resources.metalRoughImage = engine->_whiteImage->get(); - resources.metalRoughSampler = engine->_defaultSamplerLinear; + resources.metalRoughSampler = static_cast(engine->_defaultSamplerLinear); resources.dataBuffer = file.materialDataBuffer.buffer; resources.dataBufferOffset = 0; defaultMat->data = engine->metalRoughMaterial.write_material( - engine->_device, MaterialPass::MainColor, resources, + 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->getLogicalDevice(); + 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 2f2b6dc9..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,13 +16,11 @@ class ModelImpl : public IModel { /*! \brief * throws std::runtime_error() */ - ModelImpl(); - ~ModelImpl() override; + 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; @@ -32,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/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/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/Device.h b/src/include/graphics/vulkan/Device.h new file mode 100644 index 00000000..c30e6ae2 --- /dev/null +++ b/src/include/graphics/vulkan/Device.h @@ -0,0 +1,10 @@ +#pragma once +#include +#include + +struct Device { + VkPhysicalDevice physical; + vkb::Device logical; + Device(const vkb::Instance &instance, VkSurfaceKHR surface); + ~Device(); +}; 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/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/pipelines.h b/src/include/graphics/vulkan/pipelines.h index a9229949..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; @@ -37,9 +34,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); @@ -69,7 +71,6 @@ class Pipelines { VkDescriptorSetLayout drawImageDescriptorLayout, AllocatedImage drawImage); void destroy(); - private: VkDevice _device; VkDescriptorSetLayout _singleImageDescriptorLayout; diff --git a/src/include/graphics/vulkan/vk_engine.h b/src/include/graphics/vulkan/vk_engine.h index 40db0c65..aefa9242 100644 --- a/src/include/graphics/vulkan/vk_engine.h +++ b/src/include/graphics/vulkan/vk_engine.h @@ -1,10 +1,14 @@ #pragma once +#include +#include #include #include +#include #include #include #include +#include #include #include #include @@ -13,18 +17,19 @@ #include #include -#include "vk_descriptors.h" -#include "vk_types.h" -#include "vk_smart_wrappers.h" - -#include "pipelines.h" #include "ComputePipeline.h" - +#include "DescriptorSetLayout.h" +#include "Device.h" +#include "VulkanInit.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; @@ -63,7 +68,11 @@ struct DrawContext { }; class VulkanEngine { + VulkanInit info; public: + VkDevice getLogicalDevice() const { return info.getLogicalDevice(); } + VkQueue getQueue() const { return info.getQueue(); } + uint32_t getQueueIndex() const { return info.getQueueIndex(); } Pipelines pipelines; @@ -82,8 +91,6 @@ class VulkanEngine { std::unordered_map> loadedScenes; - Camera* mainCamera; - DrawContext mainDrawContext; std::unordered_map> loadedNodes; @@ -93,35 +100,21 @@ 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}; 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); - - // shuts down the engine - void cleanup(); - // 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; VkFormat _swapchainImageFormat; @@ -130,8 +123,6 @@ class VulkanEngine { std::vector _swapchainImageViews; VkExtent2D _swapchainExtent; - VmaAllocator _allocator; - std::unique_ptr _drawImage; std::unique_ptr _depthImage; VkExtent2D _drawExtent; @@ -140,7 +131,8 @@ class VulkanEngine { DescriptorAllocatorGrowable globalDescriptorAllocator; VkDescriptorSet _drawImageDescriptors; - VkDescriptorSetLayout _drawImageDescriptorLayout; + + DescriptorSetLayout _drawImageDescriptorLayout; GPUMeshBuffers rectangle; @@ -150,11 +142,11 @@ class VulkanEngine { std::vector> testMeshes; - bool resize_requested; + bool resize_requested{true}; GPUSceneData sceneData; - VkDescriptorSetLayout _gpuSceneDataDescriptorLayout; + DescriptorSetLayout _gpuSceneDataDescriptorLayout; AllocatedImage create_image(VkExtent3D size, VkFormat format, VkImageUsageFlags usage, @@ -169,10 +161,21 @@ class VulkanEngine { std::unique_ptr _greyImage; std::unique_ptr _errorCheckerboardImage; - VkSampler _defaultSamplerLinear; - VkSampler _defaultSamplerNearest; + struct Sampler { + explicit operator VkSampler() const; + void create(const VkDevice& pDevice, + const VkSamplerCreateInfo* pCreateInfo, + 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; - VkDescriptorSetLayout _singleImageDescriptorLayout; + DescriptorSetLayout _singleImageDescriptorLayout; MaterialInstance defaultData; GLTFMetallic_Roughness metalRoughMaterial; @@ -180,18 +183,26 @@ class VulkanEngine { AllocatedBuffer create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage) const; + // initializes everything in the engine + VulkanEngine(Camera& camera); + ~VulkanEngine(); + void destroy_buffer(const AllocatedBuffer& buffer) const; private: + struct Imgui { + void init(const VkDevice& dev, SDL_Window* w, + const VkInstance& pInstance, const VkPhysicalDevice& physicalDevice, + const VkQueue& queue, const VkFormat* format); + ~Imgui(); + private: + void initImguiPool(); + VkDevice _device{VK_NULL_HANDLE}; + VkDescriptorPool _imguiPool{VK_NULL_HANDLE}; + }; + Imgui _imgui; // Smart pointer collections for automatic cleanup 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); @@ -208,8 +219,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/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; }; 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