diff --git a/include/inexor/vulkan-renderer/application.hpp b/include/inexor/vulkan-renderer/application.hpp index 03aee7df6..dc63d1e72 100644 --- a/include/inexor/vulkan-renderer/application.hpp +++ b/include/inexor/vulkan-renderer/application.hpp @@ -55,6 +55,7 @@ class Application { VkDescriptorSet m_descriptor_set{VK_NULL_HANDLE}; std::shared_ptr m_octree_pipeline; + std::shared_ptr m_octree_pass; struct ModelViewPerspectiveMatrices { glm::mat4 model{1.0f}; diff --git a/include/inexor/vulkan-renderer/render-graph/graphics_pass_builder.hpp b/include/inexor/vulkan-renderer/render-graph/graphics_pass_builder.hpp index 2d68475e3..fb19b981e 100644 --- a/include/inexor/vulkan-renderer/render-graph/graphics_pass_builder.hpp +++ b/include/inexor/vulkan-renderer/render-graph/graphics_pass_builder.hpp @@ -25,6 +25,7 @@ class GraphicsPassBuilder { private: /// Add members which describe data related to graphics passes here std::function m_on_record_cmd_buffer{}; + /// The color attachments of the graphics pass std::vector m_color_attachments{}; /// The depth attachment of the graphics pass @@ -32,6 +33,10 @@ class GraphicsPassBuilder { /// The stencil attachment of the graphics pass Attachment m_stencil_attachment{}; + /// The graphics passes which are read by this graphics pass. Based on this, rendergraph can automatically determine + /// the correct pass order based on depth first search algorithm (DFS) + std::vector> m_graphics_pass_reads{}; + /// Reset all data of the graphics pass builder void reset(); @@ -68,7 +73,12 @@ class GraphicsPassBuilder { /// Build the graphics pass /// @param name The name of the graphics pass /// @return The graphics pass that was just created - [[nodiscard]] GraphicsPass build(std::string name); + [[nodiscard]] std::shared_ptr build(std::string name); + + /// Specify that this graphics pass A reads from another graphics pass B, meaning B should be rendered before A + /// @param graphics_pass The graphics pass which is read by this graphics pass + /// @return A const reference to the this pointer (allowing method calls to be chained) + [[nodiscard]] GraphicsPassBuilder &reads_from(std::weak_ptr graphics_pass); /// Set the function which will be called when the command buffer for rendering of the pass is being recorded /// @param on_record_cmd_buffer The command buffer recording function diff --git a/include/inexor/vulkan-renderer/render-graph/render_graph.hpp b/include/inexor/vulkan-renderer/render-graph/render_graph.hpp index 944dc887f..cd8a5f648 100644 --- a/include/inexor/vulkan-renderer/render-graph/render_graph.hpp +++ b/include/inexor/vulkan-renderer/render-graph/render_graph.hpp @@ -75,11 +75,11 @@ class RenderGraph { /// The graphics pass builder of the rendergraph GraphicsPassBuilder m_graphics_pass_builder{}; /// In these callback functions, the graphics passes will be created - using OnCreateGraphicsPass = std::function; + using OnCreateGraphicsPass = std::function(GraphicsPassBuilder &)>; /// The graphics pass create functions std::vector m_graphics_pass_create_functions; /// The graphics passes used in the rendergraph - std::vector m_graphics_passes; + std::vector> m_graphics_passes; /// ----------------------------------------------------------------------------------------------------------------- /// GRAPHICS PIPELINES diff --git a/include/inexor/vulkan-renderer/renderers/imgui.hpp b/include/inexor/vulkan-renderer/renderers/imgui.hpp index 85c812788..31477b2a6 100644 --- a/include/inexor/vulkan-renderer/renderers/imgui.hpp +++ b/include/inexor/vulkan-renderer/renderers/imgui.hpp @@ -32,9 +32,11 @@ class ImGuiRenderer { std::weak_ptr m_index_buffer; std::weak_ptr m_vertex_buffer; std::weak_ptr m_imgui_texture; + std::shared_ptr m_imgui_pipeline; + std::shared_ptr m_imgui_pass; + std::shared_ptr m_vertex_shader; std::shared_ptr m_fragment_shader; - std::shared_ptr m_imgui_pipeline; VkDescriptorSetLayout m_descriptor_set_layout{VK_NULL_HANDLE}; VkDescriptorSet m_descriptor_set{VK_NULL_HANDLE}; diff --git a/src/vulkan-renderer/application.cpp b/src/vulkan-renderer/application.cpp index 1e1ed8a8b..da8cb0824 100644 --- a/src/vulkan-renderer/application.cpp +++ b/src/vulkan-renderer/application.cpp @@ -419,23 +419,16 @@ void Application::run() { } void Application::setup_render_graph() { - // TODO: Move to OctreeRenderer and ImGuiRenderer! - // TODO: Move this to rendergraph header file? (Can we then use it here?) - using render_graph::BufferType; - using render_graph::BufferType::INDEX_BUFFER; - using render_graph::BufferType::UNIFORM_BUFFER; - using render_graph::BufferType::VERTEX_BUFFER; - using render_graph::TextureUsage::BACK_BUFFER; - using render_graph::TextureUsage::DEPTH_STENCIL_BUFFER; - const auto swapchain_extent = m_swapchain->extent(); - m_back_buffer = m_render_graph->add_texture("Color", BACK_BUFFER, m_swapchain->image_format(), - swapchain_extent.width, swapchain_extent.height); + m_back_buffer = + m_render_graph->add_texture("Color", render_graph::TextureUsage::BACK_BUFFER, m_swapchain->image_format(), + swapchain_extent.width, swapchain_extent.height); - m_depth_buffer = m_render_graph->add_texture("Depth", DEPTH_STENCIL_BUFFER, VK_FORMAT_D32_SFLOAT_S8_UINT, - swapchain_extent.width, swapchain_extent.height); + m_depth_buffer = + m_render_graph->add_texture("Depth", render_graph::TextureUsage::DEPTH_STENCIL_BUFFER, + VK_FORMAT_D32_SFLOAT_S8_UINT, swapchain_extent.width, swapchain_extent.height); - m_vertex_buffer = m_render_graph->add_buffer("Octree", VERTEX_BUFFER, [&]() { + m_vertex_buffer = m_render_graph->add_buffer("Octree", render_graph::BufferType::VERTEX_BUFFER, [&]() { // If the key N was pressed once, generate a new octree if (m_input_data->was_key_pressed_once(GLFW_KEY_N)) { load_octree_geometry(false); @@ -452,7 +445,7 @@ void Application::setup_render_graph() { // Note that the index buffer is updated together with the vertex buffer to keep data consistent // This means for m_index_buffer, on_init and on_update are defaulted to std::nullopt here! - m_index_buffer = m_render_graph->add_buffer("Octree", INDEX_BUFFER, [&]() { + m_index_buffer = m_render_graph->add_buffer("Octree", render_graph::BufferType::INDEX_BUFFER, [&]() { // Request update of the octree index buffer m_index_buffer.lock()->request_update(m_octree_indices); }); @@ -464,32 +457,27 @@ void Application::setup_render_graph() { m_vertex_buffer.lock()->request_update(m_octree_vertices); m_index_buffer.lock()->request_update(m_octree_indices); - m_uniform_buffer = m_render_graph->add_buffer("Matrices", UNIFORM_BUFFER, [&]() { + m_uniform_buffer = m_render_graph->add_buffer("Matrices", render_graph::BufferType::UNIFORM_BUFFER, [&]() { m_mvp_matrices.view = m_camera->view_matrix(); m_mvp_matrices.proj = m_camera->perspective_matrix(); m_mvp_matrices.proj[1][1] *= -1; m_uniform_buffer.lock()->request_update(m_mvp_matrices); }); - using wrapper::descriptors::DescriptorSetAllocator; - using wrapper::descriptors::DescriptorSetLayoutBuilder; - using wrapper::descriptors::DescriptorSetUpdateBuilder; m_render_graph->add_resource_descriptor( - [&](DescriptorSetLayoutBuilder &builder) { + [&](wrapper::descriptors::DescriptorSetLayoutBuilder &builder) { m_descriptor_set_layout = builder.add_uniform_buffer(VK_SHADER_STAGE_VERTEX_BIT).build("Octree"); }, - [&](DescriptorSetAllocator &allocator) { + [&](wrapper::descriptors::DescriptorSetAllocator &allocator) { m_descriptor_set = allocator.allocate("Octree", m_descriptor_set_layout); }, - [&](DescriptorSetUpdateBuilder &builder) { + [&](wrapper::descriptors::DescriptorSetUpdateBuilder &builder) { builder.add_uniform_buffer_update(m_descriptor_set, m_uniform_buffer).update(); }); - // TODO: Move octree renderer out of here - // TODO: How to associate data of rendergraph with renderers? Should renderers only do the setup? - // TODO: API style like m_render_graph->add_renderer(octree_renderer)->add_renderer(imgui_renderer);? - using wrapper::pipelines::GraphicsPipelineBuilder; - m_render_graph->add_graphics_pipeline([&](GraphicsPipelineBuilder &builder) { + // TODO: Implement octree renderer + + m_render_graph->add_graphics_pipeline([&](wrapper::pipelines::GraphicsPipelineBuilder &builder) { m_octree_pipeline = builder .set_vertex_input_bindings({ { @@ -498,8 +486,7 @@ void Application::setup_render_graph() { .inputRate = VK_VERTEX_INPUT_RATE_VERTEX, }, }) - // TODO! Fix me! - .set_multisampling(m_device->get_max_usable_sample_count(), 1.0f) + .set_multisampling(m_device->get_max_usable_sample_count(), 0.25f) .add_default_color_blend_attachment() .add_color_attachment(m_swapchain->image_format()) .set_depth_attachment_format(VK_FORMAT_D32_SFLOAT_S8_UINT) @@ -523,10 +510,10 @@ void Application::setup_render_graph() { .uses_shader(m_octree_vert) .uses_shader(m_octree_frag) .build("Octree"); + return m_octree_pipeline; }); - using wrapper::commands::CommandBuffer; - auto on_record_cmd_buffer = [&](const CommandBuffer &cmd_buf) { + auto on_record_cmd_buffer = [&](const wrapper::commands::CommandBuffer &cmd_buf) { cmd_buf.bind_pipeline(m_octree_pipeline) .bind_descriptor_set(m_descriptor_set, m_octree_pipeline) .bind_vertex_buffer(m_vertex_buffer) @@ -534,12 +521,12 @@ void Application::setup_render_graph() { .draw_indexed(static_cast(m_octree_indices.size())); }; - using render_graph::GraphicsPassBuilder; - m_render_graph->add_graphics_pass([&](GraphicsPassBuilder &builder) { - return builder.add_color_attachment(m_back_buffer, VkClearValue{1.0f, 0.0f, 0.0f, 1.0f}) - .add_depth_attachment(m_depth_buffer) - .set_on_record(std::move(on_record_cmd_buffer)) - .build("Octree"); + m_render_graph->add_graphics_pass([&](render_graph::GraphicsPassBuilder &builder) { + m_octree_pass = builder.add_color_attachment(m_back_buffer, VkClearValue{1.0f, 0.0f, 0.0f, 1.0f}) + .add_depth_attachment(m_depth_buffer) + .set_on_record(std::move(on_record_cmd_buffer)) + .build("Octree"); + return m_octree_pass; }); // TODO: We don't need to recreate the imgui overlay when swapchain is recreated, use a .recreate() method instead? diff --git a/src/vulkan-renderer/render-graph/graphics_pass_builder.cpp b/src/vulkan-renderer/render-graph/graphics_pass_builder.cpp index 37146ef27..ac1998bb3 100644 --- a/src/vulkan-renderer/render-graph/graphics_pass_builder.cpp +++ b/src/vulkan-renderer/render-graph/graphics_pass_builder.cpp @@ -1,5 +1,7 @@ #include "inexor/vulkan-renderer/render-graph/graphics_pass_builder.hpp" +#include + namespace inexor::vulkan_renderer::render_graph { GraphicsPassBuilder::GraphicsPassBuilder() { @@ -10,7 +12,7 @@ GraphicsPassBuilder &GraphicsPassBuilder::add_color_attachment(std::weak_ptr clear_value) { if (color_attachment.expired()) { throw std::invalid_argument( - "[GraphicsPassBuilder::add_color_attachment] Error: 'color_attachment' is expired!"); + "[GraphicsPassBuilder::add_color_attachment] Error: 'color_attachment' is an invalid pointer!"); } m_color_attachments.emplace_back(std::move(color_attachment), std::move(clear_value)); return *this; @@ -19,7 +21,8 @@ GraphicsPassBuilder &GraphicsPassBuilder::add_color_attachment(std::weak_ptr depth_attachment, std::optional clear_value) { if (depth_attachment.expired()) { - throw std::invalid_argument("[GraphicsPassBuilder::enable_depth_test] Error: 'depth_buffer' is expired!"); + throw std::invalid_argument( + "[GraphicsPassBuilder::enable_depth_test] Error: 'depth_buffer' is an invalid pointer!"); } m_depth_attachment = Attachment(std::move(depth_attachment), std::move(clear_value)); return *this; @@ -29,25 +32,34 @@ GraphicsPassBuilder &GraphicsPassBuilder::add_stencil_attachment(std::weak_ptr clear_value) { if (stencil_attachment.expired()) { throw std::invalid_argument( - "[GraphicsPassBuilder::add_stencil_attachment] Error: 'stencil_attachment' is expired!"); + "[GraphicsPassBuilder::add_stencil_attachment] Error: 'stencil_attachment' is an invalid pointer!"); } m_stencil_attachment = Attachment(std::move(stencil_attachment), std::move(clear_value)); return *this; } -GraphicsPass GraphicsPassBuilder::build(std::string name) { - auto graphics_pass = - GraphicsPass(std::move(name), std::move(m_on_record_cmd_buffer), std::move(m_color_attachments), - std::move(m_depth_attachment), std::move(m_stencil_attachment)); +std::shared_ptr GraphicsPassBuilder::build(std::string name) { + auto graphics_pass = std::make_shared(std::move(name), std::move(m_on_record_cmd_buffer), + std::move(m_color_attachments), std::move(m_depth_attachment), + std::move(m_stencil_attachment)); reset(); return graphics_pass; } +GraphicsPassBuilder &GraphicsPassBuilder::reads_from(std::weak_ptr graphics_pass) { + if (graphics_pass.expired()) { + throw std::invalid_argument("[GraphicsPassBuilder::reads_from] Error: 'graphics_pass' is an invalid pointer!"); + } + m_graphics_pass_reads.push_back(std::move(graphics_pass)); + return *this; +} + void GraphicsPassBuilder::reset() { m_on_record_cmd_buffer = {}; m_color_attachments = {}; m_depth_attachment = {}; m_stencil_attachment = {}; + m_graphics_pass_reads.clear(); } GraphicsPassBuilder & diff --git a/src/vulkan-renderer/render-graph/render_graph.cpp b/src/vulkan-renderer/render-graph/render_graph.cpp index b9e1f0c4a..af1387184 100644 --- a/src/vulkan-renderer/render-graph/render_graph.cpp +++ b/src/vulkan-renderer/render-graph/render_graph.cpp @@ -134,32 +134,32 @@ void RenderGraph::create_rendering_infos() { }); }; // Fill the color attachments - pass.m_color_attachment_infos.reserve(pass.m_color_attachments.size()); - for (const auto &color_attachment : pass.m_color_attachments) { - pass.m_color_attachment_infos.push_back(fill_rendering_info(color_attachment)); + pass->m_color_attachment_infos.reserve(pass->m_color_attachments.size()); + for (const auto &color_attachment : pass->m_color_attachments) { + pass->m_color_attachment_infos.push_back(fill_rendering_info(color_attachment)); } // Fill the color attachment (if specified) - const bool has_depth_attachment = !pass.m_depth_attachment.first.expired(); + const bool has_depth_attachment = !pass->m_depth_attachment.first.expired(); if (has_depth_attachment) { - pass.m_depth_attachment_info = fill_rendering_info(pass.m_depth_attachment); + pass->m_depth_attachment_info = fill_rendering_info(pass->m_depth_attachment); } // Fill the stencil attachment (if specified) - const bool has_stencil_attachment = !pass.m_stencil_attachment.first.expired(); + const bool has_stencil_attachment = !pass->m_stencil_attachment.first.expired(); if (has_stencil_attachment) { - pass.m_stencil_attachment_info = fill_rendering_info(pass.m_stencil_attachment); + pass->m_stencil_attachment_info = fill_rendering_info(pass->m_stencil_attachment); } // We store all this data in the rendering info in the graphics pass itself // The advantage of this is that we don't have to fill this during the actual rendering - pass.m_rendering_info = std::move(wrapper::make_info({ + pass->m_rendering_info = std::move(wrapper::make_info({ .renderArea = { .extent = m_swapchain.extent(), }, .layerCount = 1, - .colorAttachmentCount = static_cast(pass.m_color_attachment_infos.size()), - .pColorAttachments = pass.m_color_attachment_infos.data(), - .pDepthAttachment = has_depth_attachment ? &pass.m_depth_attachment_info : nullptr, - .pStencilAttachment = has_stencil_attachment ? &pass.m_stencil_attachment_info : nullptr, + .colorAttachmentCount = static_cast(pass->m_color_attachment_infos.size()), + .pColorAttachments = pass->m_color_attachment_infos.data(), + .pDepthAttachment = has_depth_attachment ? &pass->m_depth_attachment_info : nullptr, + .pStencilAttachment = has_stencil_attachment ? &pass->m_stencil_attachment_info : nullptr, })); } } @@ -218,7 +218,7 @@ void RenderGraph::record_command_buffers(const CommandBuffer &cmd_buf, const std VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); for (const auto &pass : m_graphics_passes) { - record_command_buffer_for_pass(cmd_buf, pass); + record_command_buffer_for_pass(cmd_buf, *pass); } // Change the layout of the swapchain image to make it ready for presenting cmd_buf.change_image_layout(m_swapchain.image(img_index), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, diff --git a/src/vulkan-renderer/renderers/imgui.cpp b/src/vulkan-renderer/renderers/imgui.cpp index 6fd729736..862063b29 100644 --- a/src/vulkan-renderer/renderers/imgui.cpp +++ b/src/vulkan-renderer/renderers/imgui.cpp @@ -63,30 +63,25 @@ ImGuiRenderer::ImGuiRenderer(const Device &device, m_fragment_shader = std::make_shared(m_device, "ImGui", VK_SHADER_STAGE_FRAGMENT_BIT, "shaders/ui.frag.spv"); - using render_graph::TextureUsage; - m_imgui_texture = render_graph.add_texture("ImGui-Font", TextureUsage::NORMAL, VK_FORMAT_R8G8B8A8_UNORM, - m_font_texture_width, m_font_texture_width, [&]() { - // Initialize the ImGui font texture - m_imgui_texture.lock()->request_update(m_font_texture_data, - m_font_texture_data_size); - }); - - using wrapper::descriptors::DescriptorSetAllocator; - using wrapper::descriptors::DescriptorSetLayoutBuilder; - using wrapper::descriptors::DescriptorSetUpdateBuilder; + m_imgui_texture = render_graph.add_texture( + "ImGui-Font", render_graph::TextureUsage::NORMAL, VK_FORMAT_R8G8B8A8_UNORM, m_font_texture_width, + m_font_texture_width, [&]() { + // Initialize the ImGui font texture + m_imgui_texture.lock()->request_update(m_font_texture_data, m_font_texture_data_size); + }); + render_graph.add_resource_descriptor( - [&](DescriptorSetLayoutBuilder &builder) { + [&](wrapper::descriptors::DescriptorSetLayoutBuilder &builder) { m_descriptor_set_layout = builder.add_combined_image_sampler(VK_SHADER_STAGE_FRAGMENT_BIT).build("ImGui"); }, - [&](DescriptorSetAllocator &allocator) { + [&](wrapper::descriptors::DescriptorSetAllocator &allocator) { m_descriptor_set = allocator.allocate("ImGui", m_descriptor_set_layout); }, - [&](DescriptorSetUpdateBuilder &builder) { + [&](wrapper::descriptors::DescriptorSetUpdateBuilder &builder) { builder.add_combined_image_sampler_update(m_descriptor_set, m_imgui_texture).update(); }); - using wrapper::pipelines::GraphicsPipelineBuilder; - render_graph.add_graphics_pipeline([&](GraphicsPipelineBuilder &builder) { + render_graph.add_graphics_pipeline([&](wrapper::pipelines::GraphicsPipelineBuilder &builder) { m_imgui_pipeline = builder .set_vertex_input_bindings({ { @@ -122,10 +117,10 @@ ImGuiRenderer::ImGuiRenderer(const Device &device, .set_descriptor_set_layout(m_descriptor_set_layout) .add_push_constant_range(VK_SHADER_STAGE_VERTEX_BIT, sizeof(m_push_const_block)) .build("ImGui"); + return m_imgui_pipeline; }); - using wrapper::commands::CommandBuffer; - auto on_record_cmd_buffer = [&](const CommandBuffer &cmd_buf) { + auto on_record_cmd_buffer = [&](const wrapper::commands::CommandBuffer &cmd_buf) { const ImGuiIO &io = ImGui::GetIO(); m_push_const_block.scale = glm::vec2(2.0f / io.DisplaySize.x, 2.0f / io.DisplaySize.y); @@ -152,11 +147,12 @@ ImGuiRenderer::ImGuiRenderer(const Device &device, } }; - using render_graph::GraphicsPassBuilder; - render_graph.add_graphics_pass([&](GraphicsPassBuilder &builder) { - return builder.add_color_attachment(m_color_attachment) - .set_on_record(std::move(on_record_cmd_buffer)) - .build("ImGui"); + render_graph.add_graphics_pass([&](render_graph::GraphicsPassBuilder &builder) { + // TODO: builder.reads_from(octree_pass)..! + m_imgui_pass = builder.add_color_attachment(m_color_attachment) + .set_on_record(std::move(on_record_cmd_buffer)) + .build("ImGui"); + return m_imgui_pass; }); } diff --git a/src/vulkan-renderer/wrapper/device.cpp b/src/vulkan-renderer/wrapper/device.cpp index 320737bd9..f9dbf31b6 100644 --- a/src/vulkan-renderer/wrapper/device.cpp +++ b/src/vulkan-renderer/wrapper/device.cpp @@ -399,9 +399,10 @@ Device::Device(const Instance &inst, const auto sample_count = m_properties.limits.framebufferColorSampleCounts & m_properties.limits.framebufferDepthSampleCounts; - const std::array sample_count_flag_bits{ + const VkSampleCountFlagBits sample_count_flag_bits[] = { VK_SAMPLE_COUNT_64_BIT, VK_SAMPLE_COUNT_32_BIT, VK_SAMPLE_COUNT_16_BIT, - VK_SAMPLE_COUNT_8_BIT, VK_SAMPLE_COUNT_4_BIT, VK_SAMPLE_COUNT_2_BIT}; + VK_SAMPLE_COUNT_8_BIT, VK_SAMPLE_COUNT_4_BIT, VK_SAMPLE_COUNT_2_BIT, + }; for (const auto &sample_count_flag_bit : sample_count_flag_bits) { if (sample_count & sample_count_flag_bit) {