From e6a656be988b7dcdf3943ecdba11a4815aa2bddc Mon Sep 17 00:00:00 2001 From: Johannes Schneider Date: Thu, 18 Jul 2024 20:02:29 +0200 Subject: [PATCH] [WIP] Work in progress --- .../inexor/vulkan-renderer/application.hpp | 2 +- .../vulkan-renderer/render-graph/buffer.hpp | 21 ++-- .../render-graph/graphics_pass.hpp | 49 +++++--- .../render-graph/graphics_pass_builder.hpp | 21 +++- .../render-graph/render_graph.hpp | 40 ++++--- .../vulkan-renderer/render-graph/texture.hpp | 25 ++-- .../vulkan-renderer/renderers/imgui.hpp | 22 ++-- .../descriptor_set_update_frequency.hpp | 13 -- ...r.hpp => write_descriptor_set_builder.hpp} | 40 ++++--- .../vulkan-renderer/wrapper/sampler.hpp | 2 +- .../vulkan-renderer/wrapper/swapchain.hpp | 9 +- src/CMakeLists.txt | 2 +- src/vulkan-renderer/application.cpp | 9 +- src/vulkan-renderer/render-graph/buffer.cpp | 21 +++- .../render-graph/graphics_pass.cpp | 39 ++---- .../render-graph/graphics_pass_builder.cpp | 28 +++-- .../render-graph/render_graph.cpp | 111 +++++++++++------- src/vulkan-renderer/render-graph/texture.cpp | 20 ++-- src/vulkan-renderer/renderers/imgui.cpp | 27 +++-- ...r.cpp => write_descriptor_set_builder.cpp} | 36 +++--- 20 files changed, 308 insertions(+), 229 deletions(-) delete mode 100644 include/inexor/vulkan-renderer/wrapper/descriptors/descriptor_set_update_frequency.hpp rename include/inexor/vulkan-renderer/wrapper/descriptors/{descriptor_set_update_builder.hpp => write_descriptor_set_builder.hpp} (52%) rename src/vulkan-renderer/wrapper/descriptors/{descriptor_set_update_builder.cpp => write_descriptor_set_builder.cpp} (54%) diff --git a/include/inexor/vulkan-renderer/application.hpp b/include/inexor/vulkan-renderer/application.hpp index a5c3cf7d8..ee7318432 100644 --- a/include/inexor/vulkan-renderer/application.hpp +++ b/include/inexor/vulkan-renderer/application.hpp @@ -36,7 +36,7 @@ class Application { std::unique_ptr m_instance; std::unique_ptr m_device; std::unique_ptr m_surface; - std::unique_ptr m_swapchain; + std::shared_ptr m_swapchain; std::unique_ptr m_imgui_overlay; std::vector m_octree_vertices; diff --git a/include/inexor/vulkan-renderer/render-graph/buffer.hpp b/include/inexor/vulkan-renderer/render-graph/buffer.hpp index aa455d550..677344e06 100644 --- a/include/inexor/vulkan-renderer/render-graph/buffer.hpp +++ b/include/inexor/vulkan-renderer/render-graph/buffer.hpp @@ -20,7 +20,7 @@ class CommandBuffer; namespace inexor::vulkan_renderer::wrapper::descriptors { /// Forward declaration -class DescriptorSetUpdateBuilder; +class WriteDescriptorSetBuilder; } // namespace inexor::vulkan_renderer::wrapper::descriptors namespace inexor::vulkan_renderer::render_graph { @@ -35,21 +35,23 @@ enum class BufferType { // Forward declaration class GraphicsPass; -using inexor::vulkan_renderer::wrapper::Device; -using inexor::vulkan_renderer::wrapper::commands::CommandBuffer; -using inexor::vulkan_renderer::wrapper::descriptors::DescriptorSetUpdateBuilder; +// Using declarations +using wrapper::Device; +using wrapper::commands::CommandBuffer; +using wrapper::descriptors::WriteDescriptorSetBuilder; // TODO: Store const reference to rendergraph and retrieve the swapchain image index for automatic buffer tripling /// RAII wrapper for buffer resources inside of the rendergraph /// A buffer resource can be a vertex buffer, index buffer, or uniform buffer class Buffer { -private: + // friend class RenderGraph; friend class GraphicsPass; friend class CommandBuffer; - friend class DescriptorSetUpdateBuilder; + friend class WriteDescriptorSetBuilder; +private: /// The device wrapper const Device &m_device; /// The internal debug name of the buffer resource @@ -77,7 +79,7 @@ class Buffer { /// simplicity however, we made the update function not std::optional. // TODO: Rewrite description - std::function m_on_update; + std::function m_on_check_for_update; /// TODO: Is this is relevant for uniform buffers only? /// TODO: Maybe buffer updates should be done immediately, and no m_src_data should be stored! @@ -104,8 +106,11 @@ class Buffer { /// @param cmd_buf The command buffer void create(const CommandBuffer &cmd_buf); + /// Call destroy_buffer and destroy_staging_buffer + void destroy_all(); + /// Call vmaDestroyBuffer for the actual buffer - void destroy(); + void destroy_buffer(); /// Call vmaDestroyBuffer for the staging bufffer void destroy_staging_buffer(); diff --git a/include/inexor/vulkan-renderer/render-graph/graphics_pass.hpp b/include/inexor/vulkan-renderer/render-graph/graphics_pass.hpp index 120e972e9..9c6b12fe1 100644 --- a/include/inexor/vulkan-renderer/render-graph/graphics_pass.hpp +++ b/include/inexor/vulkan-renderer/render-graph/graphics_pass.hpp @@ -5,6 +5,7 @@ #include "inexor/vulkan-renderer/render-graph/buffer.hpp" #include "inexor/vulkan-renderer/render-graph/texture.hpp" #include "inexor/vulkan-renderer/wrapper/descriptors/descriptor_set_layout.hpp" +#include "inexor/vulkan-renderer/wrapper/swapchain.hpp" #include #include @@ -23,10 +24,21 @@ namespace inexor::vulkan_renderer::render_graph { class RenderGraph; // Using declaration +using wrapper::Swapchain; using wrapper::descriptors::DescriptorSetLayout; -/// A rendering attachment is just a texture paired with an optional clear value -using RenderingAttachment = std::pair, std::optional>; +/// A universal rendering attachment structure for color, depth, stencil attachment and swapchains +struct UniversalRenderingAttachment { + // This pointer will point to either the image view of the attachment or to the image view of the current swapchain + // image. This allows us to treat all attachment types and swapchain in a unified way. + VkImageView *img_view_ptr{nullptr}; + /// The extent of the attachment or the swapchain + VkExtent2D *extent_ptr{nullptr}; + /// The clear value of the attachment or the swapchain + std::optional clear_value{std::nullopt}; +}; + +// TODO: Move this to device wrapper(?) /// The debug label colors for vkCmdBeginDebugUtilsLabelEXT enum class DebugLabelColor { @@ -56,6 +68,7 @@ enum class DebugLabelColor { /// A wrapper for graphics passes inside of rendergraph class GraphicsPass { + // friend class RenderGraph; private: @@ -69,22 +82,25 @@ class GraphicsPass { /// The descriptor set of the pass (this will be created by rendergraph) VkDescriptorSet m_descriptor_set{VK_NULL_HANDLE}; - /// The rendering info will be filled during rendergraph compilation so we don't have to do this while rendering - VkRenderingInfo m_rendering_info{}; + /// The color of the debug label region (visible in graphics debuggers like RenderDoc) + std::array m_debug_label_color; // NOTE: The rendering info will be filled during rendergraph compilation with pointers to the members below. This // means we must make sure that the memory of the attachment infos below is still valid during rendering, which is // why we store them as members here. + /// The graphics passes this pass reads from std::vector> m_graphics_pass_reads; - /// The color attachments - std::vector m_color_attachments{}; - /// The depth attachment (only one at maximum) - RenderingAttachment m_depth_attachment{}; - /// The stencil attachment (only one at maximum) - RenderingAttachment m_stencil_attachment{}; + /// The texture attachments of this pass (unified means color, depth, stencil attachment or a swapchain) + std::vector> m_write_attachments{}; + /// The swapchains this graphics pass writes to + std::vector> m_write_swapchains{}; + + // All the data below will be filled and used by rendergraph only + /// The rendering info will be filled during rendergraph compilation so we don't have to do this while rendering + VkRenderingInfo m_rendering_info{}; /// The color attachments inside of m_rendering_info std::vector m_color_attachment_infos{}; /// The depth attachment inside of m_rendering_info @@ -92,23 +108,20 @@ class GraphicsPass { /// The stencil attachment inside of m_rendering_info VkRenderingAttachmentInfo m_stencil_attachment_info{}; - /// The color of the debug label region (visible in graphics debuggers like RenderDoc) - std::array m_debug_label_color; - public: - GraphicsPass(std::string name) : m_name(name) {} - /// Default constructor /// @param name The name of the graphics pass /// @param on_record_cmd_buffer The command buffer recording function of the graphics pass /// @param graphics_pass_reads The graphics passes this graphics pass reads from /// @param write_attachments The attachment this graphics pass writes to - /// @param color The debug label (visible in graphics debuggers like RenderDoc) + /// @param write_swapchains The swapchains this graphics pass writes to + /// @param pass_debug_label_color The debug label of the pass (visible in graphics debuggers like RenderDoc) GraphicsPass(std::string name, std::function on_record_cmd_buffer, std::vector> graphics_pass_reads, - std::vector write_attachments, - DebugLabelColor color); + std::vector> write_attachments, + std::vector> write_swapchains, + DebugLabelColor pass_debug_label_color); GraphicsPass(const GraphicsPass &) = delete; GraphicsPass(GraphicsPass &&other) noexcept; 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 8db75e664..74765acea 100644 --- a/include/inexor/vulkan-renderer/render-graph/graphics_pass_builder.hpp +++ b/include/inexor/vulkan-renderer/render-graph/graphics_pass_builder.hpp @@ -4,6 +4,7 @@ #include "inexor/vulkan-renderer/render-graph/graphics_pass.hpp" #include "inexor/vulkan-renderer/render-graph/texture.hpp" #include "inexor/vulkan-renderer/wrapper/make_info.hpp" +#include "inexor/vulkan-renderer/wrapper/swapchain.hpp" #include #include @@ -17,6 +18,7 @@ class CommandBuffer; namespace inexor::vulkan_renderer::render_graph { // Using declaration +using wrapper::Swapchain; using wrapper::commands::CommandBuffer; /// A builder class for graphics passes in the rendergraph @@ -24,12 +26,14 @@ class GraphicsPassBuilder { private: /// Add members which describe data related to graphics passes here std::function m_on_record_cmd_buffer{}; - /// The texture resources this graphics pass writes to - std::vector m_write_attachments{}; /// The graphics passes which are read by this graphics pass std::vector> m_graphics_pass_reads{}; + /// The texture resources this graphics pass writes to + std::vector> m_write_attachments{}; + /// The swapchain this graphics pass writes to + std::vector> m_write_swapchains{}; - /// Reset all data of the graphics pass builder + /// Reset the data of the graphics pass builder void reset(); public: @@ -50,9 +54,11 @@ class GraphicsPassBuilder { /// Specify that this graphics pass A reads from another graphics pass B (if the weak_ptr to B is not expired), /// meaning B should be rendered before A. It is perfect valid for 'graphics_pass' to be an invalid pointer, in /// which case the read is not added. + /// @param condition The condition under which the pass is read from /// @param graphics_pass The graphics pass (can be an invalid pointer) /// @return A const reference to the this pointer (allowing method calls to be chained) - [[nodiscard]] GraphicsPassBuilder &conditionally_reads_from(std::weak_ptr graphics_pass); + [[nodiscard]] GraphicsPassBuilder &conditionally_reads_from(std::weak_ptr graphics_pass, + bool condition); /// 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 @@ -70,6 +76,13 @@ class GraphicsPassBuilder { /// @return A const reference to the this pointer (allowing method calls to be chained) [[nodiscard]] GraphicsPassBuilder &writes_to(std::weak_ptr color_attachment, std::optional clear_value = std::nullopt); + + // TODO: Swapchain clear values? + + /// Specify that this graphics pass writes to a swapchain + /// @param swapchain The swapchain this pass writes to + /// @return A const reference to the this pointer (allowing method calls to be chained) + [[nodiscard]] GraphicsPassBuilder &writes_to(std::weak_ptr swapchain); }; } // namespace inexor::vulkan_renderer::render_graph diff --git a/include/inexor/vulkan-renderer/render-graph/render_graph.hpp b/include/inexor/vulkan-renderer/render-graph/render_graph.hpp index 030e827b4..cc83c9238 100644 --- a/include/inexor/vulkan-renderer/render-graph/render_graph.hpp +++ b/include/inexor/vulkan-renderer/render-graph/render_graph.hpp @@ -7,7 +7,7 @@ #include "inexor/vulkan-renderer/wrapper/descriptors/descriptor_set_allocator.hpp" #include "inexor/vulkan-renderer/wrapper/descriptors/descriptor_set_layout_builder.hpp" #include "inexor/vulkan-renderer/wrapper/descriptors/descriptor_set_layout_cache.hpp" -#include "inexor/vulkan-renderer/wrapper/descriptors/descriptor_set_update_builder.hpp" +#include "inexor/vulkan-renderer/wrapper/descriptors/write_descriptor_set_builder.hpp" #include "inexor/vulkan-renderer/wrapper/pipelines/pipeline.hpp" #include "inexor/vulkan-renderer/wrapper/pipelines/pipeline_builder.hpp" #include "inexor/vulkan-renderer/wrapper/pipelines/pipeline_layout.hpp" @@ -30,13 +30,14 @@ class Swapchain; namespace inexor::vulkan_renderer::render_graph { +// Using declarations using wrapper::Device; using wrapper::Swapchain; using wrapper::commands::CommandBuffer; using wrapper::descriptors::DescriptorSetAllocator; using wrapper::descriptors::DescriptorSetLayoutBuilder; using wrapper::descriptors::DescriptorSetLayoutCache; -using wrapper::descriptors::DescriptorSetUpdateBuilder; +using wrapper::descriptors::WriteDescriptorSetBuilder; using wrapper::pipelines::GraphicsPipeline; using wrapper::pipelines::GraphicsPipelineBuilder; using wrapper::pipelines::PipelineLayout; @@ -52,8 +53,6 @@ class RenderGraph { private: /// The device wrapper Device &m_device; - /// The swapchain wrapper - Swapchain &m_swapchain; // The rendergraph has its own logger std::shared_ptr m_log{spdlog::default_logger()->clone("render-graph")}; @@ -92,6 +91,13 @@ class RenderGraph { /// The graphics passes used in the rendergraph std::vector> m_graphics_passes; + /// ----------------------------------------------------------------------------------------------------------------- + /// SWAPCHAINS + /// ----------------------------------------------------------------------------------------------------------------- + + // TODO + std::vector> m_swapchains; + /// ----------------------------------------------------------------------------------------------------------------- /// GRAPHICS PIPELINES /// ----------------------------------------------------------------------------------------------------------------- @@ -199,25 +205,32 @@ class RenderGraph { /// valid memory when it is used in the on_record function! The descriptor sets are allocated by rendergraph via /// OnAllocateDescriptorSet functions using the DescriptorSetAllocator of the rendergraph. Descriptor sets are /// updated in the OnUpdateDescriptorSet functions using the DescriptorSetUpdateBuilder of the rendergraph. + /// + /// TODO: Mention batching to vkUpdateDescriptorSets... /// ----------------------------------------------------------------------------------------------------------------- /// The descriptor set layout builder (a builder pattern for descriptor set layouts) DescriptorSetLayoutBuilder m_descriptor_set_layout_builder; /// The descriptor set allocator DescriptorSetAllocator m_descriptor_set_allocator; - /// The descriptor set update builder (a builder pattern for descriptor set updates) - DescriptorSetUpdateBuilder m_descriptor_set_update_builder; + /// The write descriptor set builder (a builder pattern for write descriptor sets) + WriteDescriptorSetBuilder m_write_descriptor_set_builder; /// A user-defined function which creates the descriptor set layout using OnBuildDescriptorSetLayout = std::function; /// A user-defined function which allocates a descriptor set using OnAllocateDescriptorSet = std::function; - /// A user-defined function which updates a descriptor set - using OnUpdateDescriptorSet = std::function; + /// A user-defined function which builds the descriptor set write for the pass + using OnBuildWriteDescriptorSets = std::function(WriteDescriptorSetBuilder &)>; + /// Resource descriptors are managed by specifying those three functions to the rendergraph /// Rendergraph will then call those function in the correct order during rendergraph compilation - using ResourceDescriptor = std::tuple; + using ResourceDescriptor = + std::tuple; + /// The resource descriptors of the rendergraph std::vector m_resource_descriptors; + /// All write descriptor sets will be stored in here so we can have one batched call to vkUpdateDescriptorSets + std::vector m_write_descriptor_sets; /// Allocate the descriptor sets void allocate_descriptor_sets(); @@ -239,9 +252,6 @@ class RenderGraph { /// Create the graphics pipelines void create_graphics_pipelines(); - /// Fill the VkRenderingInfo of each graphics pass - // void create_rendering_infos(); - /// Create the textures void create_textures(); @@ -284,10 +294,10 @@ class RenderGraph { /// Default constructor /// @note device and swapchain are not taken as a const reference because rendergraph needs to modify both /// @param device The device wrapper - /// @param swapchain The swapchain wrapper - RenderGraph(Device &device, Swapchain &swapchain); + RenderGraph(Device &device); RenderGraph(const RenderGraph &) = delete; + // TODO: Implement me! RenderGraph(RenderGraph &&) noexcept; ~RenderGraph() = default; @@ -323,7 +333,7 @@ class RenderGraph { /// @param on_update_descriptor_set The descriptor set update function void add_resource_descriptor(OnBuildDescriptorSetLayout on_build_descriptor_set_layout, OnAllocateDescriptorSet on_allocate_descriptor_set, - OnUpdateDescriptorSet on_update_descriptor_set); + OnBuildWriteDescriptorSets on_update_descriptor_set); /// Add a texture which will be initialized externally (not inside of rendergraph) /// @param texture_name The name of the texture diff --git a/include/inexor/vulkan-renderer/render-graph/texture.hpp b/include/inexor/vulkan-renderer/render-graph/texture.hpp index f41c9c519..1524dc02c 100644 --- a/include/inexor/vulkan-renderer/render-graph/texture.hpp +++ b/include/inexor/vulkan-renderer/render-graph/texture.hpp @@ -21,7 +21,7 @@ class CommandBuffer; namespace inexor::vulkan_renderer::wrapper::descriptors { /// Forward declaration -class DescriptorSetUpdateBuilder; +class WriteDescriptorSetBuilder; } // namespace inexor::vulkan_renderer::wrapper::descriptors namespace inexor::vulkan_renderer::render_graph { @@ -40,15 +40,17 @@ class GraphicsPass; // Using declarations using wrapper::Device; using wrapper::commands::CommandBuffer; -using wrapper::descriptors::DescriptorSetUpdateBuilder; +using wrapper::descriptors::WriteDescriptorSetBuilder; /// RAII wrapper for texture resources class Texture { - friend class DescriptorSetUpdateBuilder; + // These friend classes are allowed to access the private data of Texture + friend class WriteDescriptorSetBuilder; friend class GraphicsPass; friend class RenderGraph; private: + /// The device wrapper const Device &m_device; /// The name of the texture std::string m_name; @@ -60,12 +62,15 @@ class Texture { std::uint32_t m_width{0}; /// The height of the texture std::uint32_t m_height{0}; + /// The channel count of the texture (4 by default) + // TODO: Can we determine the number of channels based on the given format? + std::uint32_t m_channels{4}; + /// The sample count of the MSAA image (if MSAA is enabled) + VkSampleCountFlagBits m_sample_count; /// The image of the texture std::unique_ptr m_img; - /// The sample count of the MSAA image (if MSAA is enabled) - VkSampleCountFlagBits m_sample_count; /// This is only used internally inside of rendergraph in case this texture used as a back buffer, depth buffer, or /// stencil buffer and MSAA is enabled. std::unique_ptr m_msaa_img; @@ -78,7 +83,7 @@ class Texture { /// This is for initialization of textures which are of TextureUsage::NORMAL std::optional> m_on_init{std::nullopt}; /// By definition, if this is not std::nullopt, this is a dynamic texture - std::optional> m_on_update{std::nullopt}; + std::optional> m_on_check_for_updates{std::nullopt}; // The staging buffer for updating the texture data VkBuffer m_staging_buffer{VK_NULL_HANDLE}; @@ -112,16 +117,18 @@ class Texture { /// @param height The height of the texture /// @param sample_count The sample count of the texture /// @param on_init The initialization function of the texture - /// @param on_update The update function of the texture + /// @note If ``on_init`` is ``std::nullopt``, it means the texture will be initialized internally in rendergraph. + /// @param on_check_for_updates The update function of the texture + /// @note If ``on_check_for_updates`` is ``std::nullopt``, the texture will not be updated at all! Texture(const Device &device, std::string name, TextureUsage usage, VkFormat format, std::uint32_t width, std::uint32_t height, - VkSampleCountFlagBits sample_count, + VkSampleCountFlagBits sample_count, // TODO: Channel count as well? std::optional> on_init = std::nullopt, - std::optional> on_update = std::nullopt); + std::optional> on_check_for_updates = std::nullopt); Texture(const Texture &) = delete; Texture(Texture &&other) noexcept; diff --git a/include/inexor/vulkan-renderer/renderers/imgui.hpp b/include/inexor/vulkan-renderer/renderers/imgui.hpp index 724886c60..71d8a48ae 100644 --- a/include/inexor/vulkan-renderer/renderers/imgui.hpp +++ b/include/inexor/vulkan-renderer/renderers/imgui.hpp @@ -12,20 +12,22 @@ namespace inexor::vulkan_renderer::wrapper { // Forward declarations class Device; +class Shader; class Swapchain; } // namespace inexor::vulkan_renderer::wrapper -using inexor::vulkan_renderer::wrapper::Device; -using inexor::vulkan_renderer::wrapper::Swapchain; - namespace inexor::vulkan_renderer::render_graph { // Forward declaration class RenderGraph; -class Shader; } // namespace inexor::vulkan_renderer::render_graph namespace inexor::vulkan_renderer::renderers { +// Using declarations +using wrapper::Device; +using wrapper::Shader; +using wrapper::Swapchain; + /// A wrapper for an ImGui implementation class ImGuiRenderer { std::weak_ptr m_index_buffer; @@ -34,12 +36,12 @@ class ImGuiRenderer { std::shared_ptr m_imgui_pipeline; // This is the color attachment we will write to - std::weak_ptr m_color_attachment; + std::weak_ptr m_swapchain; // This is the previous pass we read from std::weak_ptr m_previous_pass; - std::shared_ptr m_vertex_shader; - std::shared_ptr m_fragment_shader; + std::shared_ptr m_vertex_shader; + std::shared_ptr m_fragment_shader; VkDescriptorSetLayout m_descriptor_set_layout{VK_NULL_HANDLE}; VkDescriptorSet m_descriptor_set{VK_NULL_HANDLE}; @@ -64,8 +66,6 @@ class ImGuiRenderer { /// It will be called at the beginning of set_on_update std::function m_on_update_user_data{[]() {}}; - ImDrawData *m_draw_data{nullptr}; - void load_font_data_from_file(); /// Customize ImGui style like text color for example @@ -74,13 +74,13 @@ class ImGuiRenderer { public: /// Default constructor /// @param device The device wrapper - /// @param swapchain The swapchain wrapper + /// @param swapchain The swapchain to render to /// @param render_graph The rendergraph /// @param previous_pass The previous pass /// @param color_attachment The color attachment /// @param on_update_user_data The user-specified ImGui update function ImGuiRenderer(const Device &device, - const Swapchain &swapchain, + std::weak_ptr swapchain, std::weak_ptr render_graph, std::weak_ptr previous_pass, std::weak_ptr color_attachment, diff --git a/include/inexor/vulkan-renderer/wrapper/descriptors/descriptor_set_update_frequency.hpp b/include/inexor/vulkan-renderer/wrapper/descriptors/descriptor_set_update_frequency.hpp deleted file mode 100644 index 2975f529c..000000000 --- a/include/inexor/vulkan-renderer/wrapper/descriptors/descriptor_set_update_frequency.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -namespace inexor::vulkan_renderer::wrapper::descriptors { - -/// In Vulkan, it is recommended to keep VkDescriptorSets grouped by their update frequency. -enum class DescriptorSetUpdateFrequency { - STATIC, // Descriptor sets with infrequent updates or no updates at all - PER_BATCH, // Descriptor sets updated per batch - PER_FRAME, // Descriptor sets updated once per frame - DYNAMIC, // Descriptor sets updated frequently (multiple times per frame) -}; - -} // namespace inexor::vulkan_renderer::render_graph diff --git a/include/inexor/vulkan-renderer/wrapper/descriptors/descriptor_set_update_builder.hpp b/include/inexor/vulkan-renderer/wrapper/descriptors/write_descriptor_set_builder.hpp similarity index 52% rename from include/inexor/vulkan-renderer/wrapper/descriptors/descriptor_set_update_builder.hpp rename to include/inexor/vulkan-renderer/wrapper/descriptors/write_descriptor_set_builder.hpp index a1ef02b36..0981b0244 100644 --- a/include/inexor/vulkan-renderer/wrapper/descriptors/descriptor_set_update_builder.hpp +++ b/include/inexor/vulkan-renderer/wrapper/descriptors/write_descriptor_set_builder.hpp @@ -17,46 +17,50 @@ enum class BufferType; class Texture; } // namespace inexor::vulkan_renderer::render_graph -using inexor::vulkan_renderer::render_graph::Buffer; -using inexor::vulkan_renderer::render_graph::BufferType; -using inexor::vulkan_renderer::render_graph::Texture; - namespace inexor::vulkan_renderer::wrapper::descriptors { +// Using declarations +using render_graph::Buffer; +using render_graph::BufferType; +using render_graph::Texture; + /// A wrapper class for batching calls to vkUpdateDescriptorSets -class DescriptorSetUpdateBuilder { +class WriteDescriptorSetBuilder { private: const Device &m_device; - std::vector m_write_sets; + std::vector m_write_descriptor_sets; std::uint32_t m_binding{0}; + /// Reset the data of the builder so it can be re-used + void reset(); + public: /// Default constructor /// @param device The device wrapper - DescriptorSetUpdateBuilder(const Device &device); + explicit WriteDescriptorSetBuilder(const Device &device); - DescriptorSetUpdateBuilder(const DescriptorSetUpdateBuilder &) = default; - DescriptorSetUpdateBuilder(DescriptorSetUpdateBuilder &&) noexcept; - ~DescriptorSetUpdateBuilder() = default; + WriteDescriptorSetBuilder(const WriteDescriptorSetBuilder &) = default; + WriteDescriptorSetBuilder(WriteDescriptorSetBuilder &&) noexcept; + ~WriteDescriptorSetBuilder() = default; - DescriptorSetUpdateBuilder &operator=(const DescriptorSetUpdateBuilder &) = default; - DescriptorSetUpdateBuilder &operator=(DescriptorSetUpdateBuilder &&) noexcept; + WriteDescriptorSetBuilder &operator=(const WriteDescriptorSetBuilder &) = default; + WriteDescriptorSetBuilder &operator=(WriteDescriptorSetBuilder &&) = delete; /// Add a write descriptor set for a uniform buffer /// @param descriptor_set The destination descriptor set /// @param buffer The rendergraph uniform buffer /// @return A reference to the dereferenced ``this`` pointer - DescriptorSetUpdateBuilder &add_uniform_buffer_update(VkDescriptorSet descriptor_set, std::weak_ptr buffer); + WriteDescriptorSetBuilder &add_uniform_buffer_update(VkDescriptorSet descriptor_set, std::weak_ptr buffer); /// Add a write descriptor set for a combined image sampler /// @param descriptor_set The destination descriptor set /// @param texture The rendergraph texture /// @return A reference to the dereferenced ``this`` pointer - DescriptorSetUpdateBuilder &add_combined_image_sampler_update(VkDescriptorSet descriptor_set, - std::weak_ptr texture); - - /// Call vkUpdateDescriptorSets - void update(); + WriteDescriptorSetBuilder &add_combined_image_sampler_update(VkDescriptorSet descriptor_set, + std::weak_ptr texture); + /// Return the write descriptor sets and reset the builder + /// @return A std::vector of VkWriteDescriptorSet + [[nodiscard]] std::vector build(); }; } // namespace inexor::vulkan_renderer::wrapper::descriptors diff --git a/include/inexor/vulkan-renderer/wrapper/sampler.hpp b/include/inexor/vulkan-renderer/wrapper/sampler.hpp index 0c108fafb..8b0f5b119 100644 --- a/include/inexor/vulkan-renderer/wrapper/sampler.hpp +++ b/include/inexor/vulkan-renderer/wrapper/sampler.hpp @@ -39,7 +39,7 @@ class Sampler { Sampler(const Device &device, std::string name, const VkSamplerCreateInfo &sampler_ci = make_info({ - // These are the default sampler settings + // NOTE: These are the default sampler settings .magFilter = VK_FILTER_LINEAR, .minFilter = VK_FILTER_LINEAR, .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, diff --git a/include/inexor/vulkan-renderer/wrapper/swapchain.hpp b/include/inexor/vulkan-renderer/wrapper/swapchain.hpp index f337a0a65..1dd3556df 100644 --- a/include/inexor/vulkan-renderer/wrapper/swapchain.hpp +++ b/include/inexor/vulkan-renderer/wrapper/swapchain.hpp @@ -16,6 +16,7 @@ class Semaphore; namespace inexor::vulkan_renderer::render_graph { // Forward declaration class RenderGraph; +class GraphicsPassBuilder; } // namespace inexor::vulkan_renderer::render_graph namespace inexor::vulkan_renderer::wrapper { @@ -23,9 +24,15 @@ namespace inexor::vulkan_renderer::wrapper { // Forward declaration class Device; +// Using declarations +using render_graph::GraphicsPassBuilder; +using render_graph::RenderGraph; + /// RAII wrapper class for swapchains class Swapchain { - friend class render_graph::RenderGraph; + // + friend class RenderGraph; + friend class GraphicsPassBuilder; private: Device &m_device; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c932aa2df..e9bd4f2de 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -43,10 +43,10 @@ set(INEXOR_SOURCE_FILES vulkan-renderer/wrapper/descriptors/descriptor_pool.cpp vulkan-renderer/wrapper/descriptors/descriptor_pool_allocator.cpp vulkan-renderer/wrapper/descriptors/descriptor_set_allocator.cpp - vulkan-renderer/wrapper/descriptors/descriptor_set_update_builder.cpp vulkan-renderer/wrapper/descriptors/descriptor_set_layout.cpp vulkan-renderer/wrapper/descriptors/descriptor_set_layout_builder.cpp vulkan-renderer/wrapper/descriptors/descriptor_set_layout_cache.cpp + vulkan-renderer/wrapper/descriptors/write_descriptor_set_builder.cpp vulkan-renderer/wrapper/pipelines/pipeline.cpp vulkan-renderer/wrapper/pipelines/pipeline_builder.cpp diff --git a/src/vulkan-renderer/application.cpp b/src/vulkan-renderer/application.cpp index 2ecf492dc..d4fe7cf2f 100644 --- a/src/vulkan-renderer/application.cpp +++ b/src/vulkan-renderer/application.cpp @@ -9,7 +9,6 @@ #include "inexor/vulkan-renderer/vk_tools/enumerate.hpp" #include "inexor/vulkan-renderer/world/collision.hpp" #include "inexor/vulkan-renderer/wrapper/descriptors/descriptor_set_layout_builder.hpp" -#include "inexor/vulkan-renderer/wrapper/descriptors/descriptor_set_update_frequency.hpp" #include "inexor/vulkan-renderer/wrapper/instance.hpp" #include "inexor/vulkan-renderer/wrapper/pipelines/pipeline_layout.hpp" @@ -385,7 +384,7 @@ void Application::recreate_swapchain() { // TODO: Unified API style like this: m_device->create_rendergraph(m_swapchain); // TODO: Maybe make RenderGraph constructor (and others) private and only allow device wrapper to call it? - m_render_graph = std::make_unique(*m_device, *m_swapchain); + m_render_graph = std::make_unique(*m_device); setup_render_graph(); } @@ -472,8 +471,8 @@ void Application::setup_render_graph() { [&](wrapper::descriptors::DescriptorSetAllocator &allocator) { m_descriptor_set = allocator.allocate("Octree", m_descriptor_set_layout); }, - [&](wrapper::descriptors::DescriptorSetUpdateBuilder &builder) { - builder.add_uniform_buffer_update(m_descriptor_set, m_uniform_buffer).update(); + [&](wrapper::descriptors::WriteDescriptorSetBuilder &builder) { + return builder.add_uniform_buffer_update(m_descriptor_set, m_uniform_buffer).build(); }); m_render_graph->add_graphics_pipeline([&](wrapper::pipelines::GraphicsPipelineBuilder &builder) { @@ -534,7 +533,7 @@ void Application::setup_render_graph() { // TODO: We don't need to recreate the imgui overlay when swapchain is recreated, use a .recreate() method instead? // TODO: Decouple ImGuiRenderer form ImGuiLoader - m_imgui_overlay = std::make_unique(*m_device, *m_swapchain, m_render_graph, m_octree_pass, + m_imgui_overlay = std::make_unique(*m_device, m_swapchain, m_render_graph, m_octree_pass, m_color_attachment, [&]() { update_imgui_overlay(); }); m_render_graph->compile(); diff --git a/src/vulkan-renderer/render-graph/buffer.cpp b/src/vulkan-renderer/render-graph/buffer.cpp index c9f4affd2..7e673028c 100644 --- a/src/vulkan-renderer/render-graph/buffer.cpp +++ b/src/vulkan-renderer/render-graph/buffer.cpp @@ -11,16 +11,18 @@ namespace inexor::vulkan_renderer::render_graph { Buffer::Buffer(const Device &device, std::string buffer_name, BufferType buffer_type, std::function on_update) - : m_device(device), m_name(std::move(buffer_name)), m_on_update(std::move(on_update)), m_buffer_type(buffer_type) { + : m_device(device), m_name(std::move(buffer_name)), m_on_check_for_update(std::move(on_update)), + m_buffer_type(buffer_type) { if (m_name.empty()) { throw std::invalid_argument("[Buffer::Buffer] Error: Parameter 'name' is empty!"); } } Buffer::Buffer(Buffer &&other) noexcept : m_device(other.m_device) { + // TODO: Check me! m_name = std::move(other.m_name); m_buffer_type = other.m_buffer_type; - m_on_update = std::move(other.m_on_update); + m_on_check_for_update = std::move(other.m_on_check_for_update); m_buffer = std::exchange(other.m_buffer, VK_NULL_HANDLE); m_alloc = std::exchange(other.m_alloc, VK_NULL_HANDLE); m_alloc_info = other.m_alloc_info; @@ -33,7 +35,7 @@ Buffer::Buffer(Buffer &&other) noexcept : m_device(other.m_device) { } Buffer::~Buffer() { - destroy(); + destroy_all(); } void Buffer::create(const CommandBuffer &cmd_buf) { @@ -147,13 +149,22 @@ void Buffer::create(const CommandBuffer &cmd_buf) { }; // The update is finished m_update_requested = false; + + // NOTE: The staging buffer needs to stay valid until command buffer finished executing! + // It will be destroyed either in the destructor or the next time create is called. + + // NOTE: Another option would have been to wrap each call to create() into its own single time command buffer, which + // would increase the total number of command buffer submissions though. +} + +void Buffer::destroy_all() { + destroy_staging_buffer(); } -void Buffer::destroy() { +void Buffer::destroy_buffer() { vmaDestroyBuffer(m_device.allocator(), m_buffer, m_alloc); m_buffer = VK_NULL_HANDLE; m_alloc = VK_NULL_HANDLE; - destroy_staging_buffer(); } void Buffer::destroy_staging_buffer() { diff --git a/src/vulkan-renderer/render-graph/graphics_pass.cpp b/src/vulkan-renderer/render-graph/graphics_pass.cpp index 91e1e1c11..032082dfa 100644 --- a/src/vulkan-renderer/render-graph/graphics_pass.cpp +++ b/src/vulkan-renderer/render-graph/graphics_pass.cpp @@ -4,6 +4,7 @@ namespace inexor::vulkan_renderer::render_graph { +// TODO: Move to device wrapper? std::array get_debug_label_color(const DebugLabelColor color) { switch (color) { case DebugLabelColor::RED: @@ -48,46 +49,26 @@ std::array get_debug_label_color(const DebugLabelColor color) { GraphicsPass::GraphicsPass(std::string name, std::function on_record_cmd_buffer, std::vector> graphics_pass_reads, - std::vector write_attachments, - const DebugLabelColor color) { + std::vector> write_attachments, + std::vector> write_swapchains, + const DebugLabelColor pass_debug_label_color) { m_name = std::move(name); m_on_record_cmd_buffer = std::move(on_record_cmd_buffer); - m_debug_label_color = get_debug_label_color(color); + m_debug_label_color = get_debug_label_color(pass_debug_label_color); m_graphics_pass_reads = std::move(graphics_pass_reads); - - // As internal validation, we check if each graphics pass has only one depth attachment at maximum and only one - // stencil attachment at maximum. Note that the graphcis pass is allowed to have as many color attachments as it - // wants to. - bool depth_attachment_found{false}; - - // Sort the attachment writes by type and make sure depth buffer is not specified more than once at maximum - for (auto &write_attachment : write_attachments) { - switch (write_attachment.first.lock()->m_usage) { - case TextureUsage::NORMAL: - case TextureUsage::BACK_BUFFER: { - m_color_attachments.emplace_back(std::move(write_attachment)); - break; - } - case TextureUsage::DEPTH_STENCIL_BUFFER: { - if (depth_attachment_found) { - throw std::runtime_error( - "[GraphicsPass::GraphicsPass] Error: Duplicate depth stencil buffer specified!"); - } - depth_attachment_found = true; - m_depth_attachment = std::move(write_attachment); - break; - } - } - } - // TODO: Support for stencil buffers + m_write_attachments = std::move(write_attachments); + m_write_swapchains = std::move(write_swapchains); } GraphicsPass::GraphicsPass(GraphicsPass &&other) noexcept { + // TODO: Check me! m_name = std::move(other.m_name); m_on_record_cmd_buffer = std::move(other.m_on_record_cmd_buffer); m_descriptor_set_layout = std::exchange(other.m_descriptor_set_layout, nullptr); m_descriptor_set = std::exchange(other.m_descriptor_set, VK_NULL_HANDLE); m_rendering_info = std::move(other.m_rendering_info); + m_write_attachments = std::move(other.m_write_attachments); + m_write_swapchains = std::move(other.m_write_swapchains); m_color_attachment_infos = std::move(other.m_color_attachment_infos); m_depth_attachment_info = std::move(other.m_depth_attachment_info); m_stencil_attachment_info = std::move(other.m_stencil_attachment_info); diff --git a/src/vulkan-renderer/render-graph/graphics_pass_builder.cpp b/src/vulkan-renderer/render-graph/graphics_pass_builder.cpp index c2c4660e7..0696df079 100644 --- a/src/vulkan-renderer/render-graph/graphics_pass_builder.cpp +++ b/src/vulkan-renderer/render-graph/graphics_pass_builder.cpp @@ -11,22 +11,24 @@ GraphicsPassBuilder::GraphicsPassBuilder() { GraphicsPassBuilder::GraphicsPassBuilder(GraphicsPassBuilder &&other) noexcept { m_on_record_cmd_buffer = std::move(other.m_on_record_cmd_buffer); m_write_attachments = std::move(other.m_write_attachments); + m_write_swapchains = std::move(other.m_write_swapchains); m_graphics_pass_reads = std::move(other.m_graphics_pass_reads); } -std::shared_ptr GraphicsPassBuilder::build(std::string name, const DebugLabelColor color) { - auto graphics_pass = - std::make_shared(std::move(name), std::move(m_on_record_cmd_buffer), - std::move(m_graphics_pass_reads), std::move(m_write_attachments), color); +std::shared_ptr GraphicsPassBuilder::build(std::string name, const DebugLabelColor pass_debug_color) { + auto graphics_pass = std::make_shared( + std::move(name), std::move(m_on_record_cmd_buffer), std::move(m_graphics_pass_reads), + std::move(m_write_attachments), std::move(m_write_swapchains), pass_debug_color); reset(); return graphics_pass; } -GraphicsPassBuilder &GraphicsPassBuilder::conditionally_reads_from(std::weak_ptr graphics_pass) { - if (!graphics_pass.expired()) { +GraphicsPassBuilder &GraphicsPassBuilder::conditionally_reads_from(std::weak_ptr graphics_pass, + const bool condition) { + if (!graphics_pass.expired() && condition) { m_graphics_pass_reads.push_back(std::move(graphics_pass)); } - // NOTE: It's ok it graphics pass is expired, so no exception is thrown + // NOTE: No exception is thrown if this graphics pass is expired because it's an optional pass! return *this; } @@ -40,8 +42,8 @@ GraphicsPassBuilder &GraphicsPassBuilder::reads_from(std::weak_ptr void GraphicsPassBuilder::reset() { m_on_record_cmd_buffer = {}; - m_write_attachments.clear(); m_graphics_pass_reads.clear(); + m_write_attachments.clear(); } GraphicsPassBuilder & @@ -55,7 +57,15 @@ GraphicsPassBuilder &GraphicsPassBuilder::writes_to(std::weak_ptr attac if (attachment.expired()) { throw std::invalid_argument("[GraphicsPassBuilder::writes_to] Error: 'attachment' is an invalid pointer!"); } - m_write_attachments.emplace_back(std::move(attachment), std::move(clear_value)); + m_write_attachments.push_back(std::move(attachment)); + return *this; +} + +GraphicsPassBuilder &GraphicsPassBuilder::writes_to(std::weak_ptr swapchain) { + if (swapchain.expired()) { + throw std::invalid_argument("[GraphicsPassBuilder::writes_to] Error: 'swapchain' is an invalid pointer!"); + } + m_write_swapchains.push_back(std::move(swapchain)); return *this; } diff --git a/src/vulkan-renderer/render-graph/render_graph.cpp b/src/vulkan-renderer/render-graph/render_graph.cpp index cda39395d..ab8b8b561 100644 --- a/src/vulkan-renderer/render-graph/render_graph.cpp +++ b/src/vulkan-renderer/render-graph/render_graph.cpp @@ -10,10 +10,9 @@ namespace inexor::vulkan_renderer::render_graph { -RenderGraph::RenderGraph(Device &device, Swapchain &swapchain) - : m_device(device), m_swapchain(swapchain), m_graphics_pipeline_builder(device), - m_descriptor_set_layout_builder(device), m_descriptor_set_allocator(m_device), - m_descriptor_set_update_builder(m_device) {} +RenderGraph::RenderGraph(Device &device) + : m_device(device), m_graphics_pipeline_builder(device), m_descriptor_set_layout_builder(device), + m_descriptor_set_allocator(m_device), m_write_descriptor_set_builder(m_device) {} void RenderGraph::add_graphics_pass(OnCreateGraphicsPass on_pass_create) { m_graphics_pass_create_functions.emplace_back(std::move(on_pass_create)); @@ -39,7 +38,7 @@ void RenderGraph::allocate_descriptor_sets() { void RenderGraph::add_resource_descriptor(OnBuildDescriptorSetLayout on_build_descriptor_set_layout, OnAllocateDescriptorSet on_allocate_descriptor_set, - OnUpdateDescriptorSet on_update_descriptor_set) { + OnBuildWriteDescriptorSets on_update_descriptor_set) { m_resource_descriptors.emplace_back(std::move(on_build_descriptor_set_layout), std::move(on_allocate_descriptor_set), std::move(on_update_descriptor_set)); } @@ -104,10 +103,10 @@ void RenderGraph::compile() { } void RenderGraph::create_buffers() { - m_device.execute("RenderGraph::create_buffers|", [&](const CommandBuffer &cmd_buf) { + m_device.execute("[RenderGraph::create_buffers]", [&](const CommandBuffer &cmd_buf) { for (const auto &buffer : m_buffers) { - buffer->m_on_update(); - cmd_buf.set_suboperation_debug_name("Buffer:" + buffer->m_name); + buffer->m_on_check_for_update(); + cmd_buf.set_suboperation_debug_name("[Buffer:" + buffer->m_name + "]"); buffer->create(cmd_buf); } }); @@ -135,16 +134,16 @@ void RenderGraph::create_graphics_pipelines() { } void RenderGraph::create_textures() { - m_device.execute("RenderGraph::create_textures|", [&](const CommandBuffer &cmd_buf) { + m_device.execute("[RenderGraph::create_textures]", [&](const CommandBuffer &cmd_buf) { for (const auto &texture : m_textures) { if (texture->m_on_init) { - cmd_buf.set_suboperation_debug_name("Texture|Initialize:" + texture->m_name); + cmd_buf.set_suboperation_debug_name("[Texture|Initialize]:" + texture->m_name + "]"); std::invoke(texture->m_on_init.value()); } - cmd_buf.set_suboperation_debug_name("Texture|Create:" + texture->m_name); + cmd_buf.set_suboperation_debug_name("[Texture|Create]:" + texture->m_name + "]"); texture->create(); if (texture->m_usage == TextureUsage::NORMAL) { - cmd_buf.set_suboperation_debug_name("Texture|Update:" + texture->m_name); + cmd_buf.set_suboperation_debug_name("[Texture|Update]:" + texture->m_name + "]"); // Only external textures are updated, not back or depth buffers used internally in rendergraph texture->update(cmd_buf); } @@ -158,6 +157,7 @@ void RenderGraph::determine_pass_order() { } void RenderGraph::fill_graphics_pass_rendering_info(GraphicsPass &pass) { +#if 0 /// Fill VkRenderingAttachmentInfo for a given render_graph::Attachment /// @param attachment The attachment (color, depth, or stencil) /// @return VkRenderingAttachmentInfo The filled rendering info struct @@ -177,6 +177,7 @@ void RenderGraph::fill_graphics_pass_rendering_info(GraphicsPass &pass) { } }(); // Invoke the lambda, not just define it! + // TODO: Is there no easier way to determine this? auto is_integer_format = [](const VkFormat format) { switch (format) { // 8-bit integer formats @@ -257,42 +258,46 @@ void RenderGraph::fill_graphics_pass_rendering_info(GraphicsPass &pass) { }; // Reserve memory for the color attachments - pass.m_color_attachment_infos.reserve(pass.m_color_attachments.size()); + pass.m_color_attachment_infos.reserve(pass.m_write_color_attachments.size()); // Fill the color attachments - const bool has_any_color_attachment = pass.m_color_attachments.size() > 0; - for (const auto &color_attachment : pass.m_color_attachments) { + const bool has_any_color_attachment = pass.m_write_color_attachments.size() > 0; + for (const auto &color_attachment : pass.m_write_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_write_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_write_depth_attachment); pass.m_depth_attachment_info.resolveMode = VK_RESOLVE_MODE_NONE; } // Fill the stencil attachment (if specified) - const bool has_stencil_attachment = !pass.m_stencil_attachment.first.expired(); + const bool has_stencil_attachment = !pass.m_write_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_write_stencil_attachment); } + // TODO: If a pass has multiple color attachments those are multiple swapchains, does that mean we must group + // rendering by swapchains because there is no guarantee that they all have the same swapchain extent? + // Fill the rendering info of the pass pass.m_rendering_info = wrapper::make_info({ .renderArea = { - .extent = m_swapchain.extent(), + // TODO: Fix me! + //.extent = m_swapchain.extent(), }, .layerCount = 1, - .viewMask = 0, - .colorAttachmentCount = static_cast(pass.m_color_attachments.size()), + .colorAttachmentCount = static_cast(pass.m_color_attachment_infos.size()), .pColorAttachments = has_any_color_attachment ? pass.m_color_attachment_infos.data() : nullptr, .pDepthAttachment = has_depth_attachment ? &pass.m_depth_attachment_info : nullptr, .pStencilAttachment = has_stencil_attachment ? &pass.m_stencil_attachment_info : nullptr, }); +#endif } void RenderGraph::record_command_buffer_for_pass(const CommandBuffer &cmd_buf, GraphicsPass &pass) { - cmd_buf.set_suboperation_debug_name("Pass:" + pass.m_name); + cmd_buf.set_suboperation_debug_name("[Pass:" + pass.m_name + "]"); // Start a new debug label for this graphics pass (visible in graphics debuggers like RenderDoc) cmd_buf.begin_debug_label_region(pass.m_name, pass.m_debug_label_color); @@ -315,19 +320,20 @@ void RenderGraph::record_command_buffer_for_pass(const CommandBuffer &cmd_buf, G void RenderGraph::render() { update_buffers(); + update_textures(); update_descriptor_sets(); - // Acquire the next swapchain image index - m_swapchain.acquire_next_image_index(); + // TODO: Acquire next image for each swapchain used by rendergraph + // m_swapchain.acquire_next_image_index(); // TODO: Refactor this into .exec(); - const auto &cmd_buf = m_device.request_command_buffer("RenderGraph::render|"); + const auto &cmd_buf = m_device.request_command_buffer("[RenderGraph::render]"); // TODO: Is this correct? - // + // TODO: No, this should only be done for that one pass which writes to swapchain! // Transform the image layout of the swapchain (it comes in undefined layout after presenting) - cmd_buf.change_image_layout(m_swapchain.m_current_img, VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + // cmd_buf.change_image_layout(m_swapchain.m_current_img, VK_IMAGE_LAYOUT_UNDEFINED, + // VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); // Call the command buffer recording function of every pass for (auto &pass : m_graphics_passes) { @@ -337,9 +343,10 @@ void RenderGraph::render() { // TODO: Is this correct? // // Change the layout of the swapchain image to make it ready for presenting - cmd_buf.change_image_layout(m_swapchain.m_current_img, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + // cmd_buf.change_image_layout(m_swapchain.m_current_img, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); +#if 0 // TODO: Further abstract this away? const std::array stage_mask{VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; cmd_buf.submit_and_wait(wrapper::make_info({ @@ -349,7 +356,10 @@ void RenderGraph::render() { .commandBufferCount = 1, .pCommandBuffers = &cmd_buf.m_cmd_buf, })); - m_swapchain.present(); +#endif + + // TODO: Call present for each swapchain! + // m_swapchain.present(); } void RenderGraph::reset() { @@ -357,14 +367,13 @@ void RenderGraph::reset() { } void RenderGraph::update_buffers() { - m_device.execute("RenderGraph::update_buffers|", [&](const CommandBuffer &cmd_buf) { + m_device.execute("[RenderGraph::update_buffers]", [&](const CommandBuffer &cmd_buf) { for (const auto &buffer : m_buffers) { - std::invoke(buffer->m_on_update); + std::invoke(buffer->m_on_check_for_update); if (buffer->m_update_requested) { - cmd_buf.set_suboperation_debug_name("Buffer|Destroy:" + buffer->m_name); - buffer->destroy(); - cmd_buf.set_suboperation_debug_name("Buffer|Update:" + buffer->m_name); - cmd_buf.set_suboperation_debug_name("Buffer|Create:" + buffer->m_name); + cmd_buf.set_suboperation_debug_name("[Buffer|Destroy:" + buffer->m_name + "]"); + buffer->destroy_all(); + cmd_buf.set_suboperation_debug_name("[Buffer|Update:" + buffer->m_name + "]"); buffer->create(cmd_buf); } } @@ -372,23 +381,35 @@ void RenderGraph::update_buffers() { } void RenderGraph::update_descriptor_sets() { + // TODO: Return std::vector from build() and have only one call to vkUpdateDescriptorSets! + // TODO: We only need to call DescriptorSetUpdateBuilder once really during rendergraph compilation! + + // TODO: Reserve? When to clear? + m_write_descriptor_sets.clear(); + for (const auto &descriptor : m_resource_descriptors) { // Call descriptor set builder function for each descriptor - std::invoke(std::get<2>(descriptor), m_descriptor_set_update_builder); + auto write_descriptor_sets = std::invoke(std::get<2>(descriptor), m_write_descriptor_set_builder); + // Store the results + std::move(write_descriptor_sets.begin(), write_descriptor_sets.end(), + std::back_inserter(m_write_descriptor_sets)); } + // NOTE: We batch all descriptor set updates into one function call for performance + vkUpdateDescriptorSets(m_device.device(), static_cast(m_write_descriptor_sets.size()), + m_write_descriptor_sets.data(), 0, nullptr); } void RenderGraph::update_textures() { - m_device.execute("RenderGraph::update_textures|", [&](const CommandBuffer &cmd_buf) { + m_device.execute("[RenderGraph::update_textures]", [&](const CommandBuffer &cmd_buf) { for (const auto &texture : m_textures) { // Only for dynamic textures m_on_lambda which is not std::nullopt - if (texture->m_on_update) { + if (texture->m_on_check_for_updates) { if (texture->m_update_requested) { - cmd_buf.set_suboperation_debug_name("Texture|Destroy:" + texture->m_name); + cmd_buf.set_suboperation_debug_name("[Texture|Destroy:" + texture->m_name + "]"); texture->destroy(); - cmd_buf.set_suboperation_debug_name("Texture|Update:" + texture->m_name); - std::invoke(texture->m_on_update.value()); - cmd_buf.set_suboperation_debug_name("Texture|Create:" + texture->m_name); + cmd_buf.set_suboperation_debug_name("[Texture|Update:" + texture->m_name + "]"); + std::invoke(texture->m_on_check_for_updates.value()); + cmd_buf.set_suboperation_debug_name("[Texture|Create:" + texture->m_name + "]"); texture->create(); } } diff --git a/src/vulkan-renderer/render-graph/texture.cpp b/src/vulkan-renderer/render-graph/texture.cpp index 0c6312d65..cafdc3c30 100644 --- a/src/vulkan-renderer/render-graph/texture.cpp +++ b/src/vulkan-renderer/render-graph/texture.cpp @@ -19,9 +19,10 @@ Texture::Texture(const Device &device, const std::uint32_t height, const VkSampleCountFlagBits sample_count, std::optional> on_init, - std::optional> on_update) + std::optional> on_check_for_updates) : m_device(device), m_name(std::move(name)), m_usage(usage), m_format(format), m_width(width), m_height(height), - m_sample_count(sample_count), m_on_init(std::move(on_init)), m_on_update(std::move(on_update)) { + m_sample_count(sample_count), m_on_init(std::move(on_init)), + m_on_check_for_updates(std::move(on_check_for_updates)) { if (m_name.empty()) { throw std::invalid_argument("[Texture::Texture] Error: Parameter 'name' is empty!"); } @@ -32,6 +33,10 @@ Texture::Texture(const Device &device, } } +Texture::Texture(Texture &&other) noexcept : m_device(other.m_device) { + // TODO: Implement me! +} + Texture::~Texture() { destroy(); } @@ -127,8 +132,7 @@ void Texture::update(const CommandBuffer &cmd_buf) { destroy_staging_buffer(); } const auto staging_buffer_ci = wrapper::make_info({ - // TODO: How to deduce channel count? By format? - .size = m_width * m_height * 4, // 4 channels + .size = m_width * m_height * m_channels, .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, }); @@ -168,14 +172,14 @@ void Texture::update(const CommandBuffer &cmd_buf) { .imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, }; } - // The update is finished m_update_requested = false; // NOTE: The staging buffer needs to stay valid until command buffer finished executing! - // It will be destroyed either in the destructor or the next time execute_update is called - // Another option would have been to wrap each call to execute_update() into its own single time - // command buffer, which would increase the total number of command buffer submissions though + // It will be destroyed either in the destructor or the next time execute_update is called. + + // NOTE: Another option would have been to wrap each call to execute_update() into its own single time + // command buffer, which would increase the total number of command buffer submissions though. } void Texture::request_update(void *src_texture_data, const std::size_t src_texture_data_size) { diff --git a/src/vulkan-renderer/renderers/imgui.cpp b/src/vulkan-renderer/renderers/imgui.cpp index cc46afd42..29153f3e2 100644 --- a/src/vulkan-renderer/renderers/imgui.cpp +++ b/src/vulkan-renderer/renderers/imgui.cpp @@ -13,23 +13,23 @@ namespace inexor::vulkan_renderer::renderers { ImGuiRenderer::ImGuiRenderer(const Device &device, - const Swapchain &swapchain, + std::weak_ptr swapchain, std::weak_ptr render_graph, std::weak_ptr previous_pass, std::weak_ptr color_attachment, std::function on_update_user_data) : m_on_update_user_data(std::move(on_update_user_data)), m_previous_pass(std::move(previous_pass)), - m_color_attachment(std::move(color_attachment)) { + m_swapchain(std::move(swapchain)) { if (render_graph.expired()) { throw std::invalid_argument( "[ImGuiRenderer::ImGuiRenderer] Error: Parameter 'render_graph' is an invalid pointer!"); } - if (m_color_attachment.expired()) { + if (m_swapchain.expired()) { throw std::invalid_argument( - "[ImGuiRenderer::ImGuiRenderer] Error: Parameter 'color_attachment' is an invalid pointer!"); + "[ImGuiRenderer::ImGuiRenderer] Error: Parameter 'm_swapchain' is an invalid pointer!"); } - // NOTE: As we check below, previous_pass is allowed to be an invalid pointer (so there is no previous pass!) + // NOTE: It's valid for previous_pass to be an invalid pointer (in that case there is no previous pass!) spdlog::trace("Creating ImGui context"); ImGui::CreateContext(); @@ -41,6 +41,7 @@ ImGuiRenderer::ImGuiRenderer(const Device &device, set_imgui_style(); auto graph = render_graph.lock(); + auto target_swapchain = m_swapchain.lock(); using render_graph::BufferType; m_vertex_buffer = graph->add_buffer("ImGui|Vertex", BufferType::VERTEX_BUFFER, [&]() { @@ -92,8 +93,8 @@ ImGuiRenderer::ImGuiRenderer(const Device &device, [&](wrapper::descriptors::DescriptorSetAllocator &allocator) { m_descriptor_set = allocator.allocate("ImGui", m_descriptor_set_layout); }, - [&](wrapper::descriptors::DescriptorSetUpdateBuilder &builder) { - builder.add_combined_image_sampler_update(m_descriptor_set, m_imgui_texture).update(); + [&](wrapper::descriptors::WriteDescriptorSetBuilder &builder) -> std::vector { + return builder.add_combined_image_sampler_update(m_descriptor_set, m_imgui_texture).build(); }); graph->add_graphics_pipeline([&](wrapper::pipelines::GraphicsPipelineBuilder &builder) { @@ -123,10 +124,10 @@ ImGuiRenderer::ImGuiRenderer(const Device &device, }, }) .add_default_color_blend_attachment() - .add_color_attachment(swapchain.image_format()) + .add_color_attachment(swapchain.lock()->image_format()) .set_depth_attachment_format(VK_FORMAT_D32_SFLOAT_S8_UINT) - .set_viewport(swapchain.extent()) - .set_scissor(swapchain.extent()) + .set_viewport(swapchain.lock()->extent()) + .set_scissor(swapchain.lock()->extent()) .uses_shader(m_vertex_shader) .uses_shader(m_fragment_shader) .set_descriptor_set_layout(m_descriptor_set_layout) @@ -139,8 +140,8 @@ ImGuiRenderer::ImGuiRenderer(const Device &device, // NOTE: ImGui does not write to depth buffer and it reads from octree pass (previous pass) // NOTE: We directly return the ImGui graphics pass and do not store it in here because it's the last pass (for // now) and there is no reads_from function which would need it. - return builder.writes_to(m_color_attachment) - .conditionally_reads_from(m_previous_pass) + return builder.writes_to(swapchain) + .conditionally_reads_from(m_previous_pass, !m_previous_pass.expired()) .set_on_record([&](const wrapper::commands::CommandBuffer &cmd_buf) { ImDrawData *draw_data = ImGui::GetDrawData(); if (draw_data == nullptr || draw_data->TotalIdxCount == 0 || draw_data->TotalVtxCount == 0) { @@ -156,6 +157,8 @@ ImGuiRenderer::ImGuiRenderer(const Device &device, .bind_descriptor_set(m_descriptor_set, m_imgui_pipeline) .push_constant(m_imgui_pipeline, m_push_const_block, VK_SHADER_STAGE_VERTEX_BIT); + // TODO: Crop to ImGui viewport and scissor for improved performance + std::uint32_t index_offset = 0; std::int32_t vertex_offset = 0; for (std::size_t i = 0; i < draw_data->CmdListsCount; i++) { diff --git a/src/vulkan-renderer/wrapper/descriptors/descriptor_set_update_builder.cpp b/src/vulkan-renderer/wrapper/descriptors/write_descriptor_set_builder.cpp similarity index 54% rename from src/vulkan-renderer/wrapper/descriptors/descriptor_set_update_builder.cpp rename to src/vulkan-renderer/wrapper/descriptors/write_descriptor_set_builder.cpp index b60cf11a9..b00c845c2 100644 --- a/src/vulkan-renderer/wrapper/descriptors/descriptor_set_update_builder.cpp +++ b/src/vulkan-renderer/wrapper/descriptors/write_descriptor_set_builder.cpp @@ -1,4 +1,4 @@ -#include "inexor/vulkan-renderer/wrapper/descriptors/descriptor_set_update_builder.hpp" +#include "inexor/vulkan-renderer/wrapper/descriptors/write_descriptor_set_builder.hpp" #include "inexor/vulkan-renderer/render-graph/buffer.hpp" #include "inexor/vulkan-renderer/render-graph/texture.hpp" @@ -9,20 +9,21 @@ namespace inexor::vulkan_renderer::wrapper::descriptors { -DescriptorSetUpdateBuilder::DescriptorSetUpdateBuilder(const Device &device) : m_device(device) {} +WriteDescriptorSetBuilder::WriteDescriptorSetBuilder(const Device &device) : m_device(device) {} -DescriptorSetUpdateBuilder::DescriptorSetUpdateBuilder(DescriptorSetUpdateBuilder &&other) noexcept +WriteDescriptorSetBuilder::WriteDescriptorSetBuilder(WriteDescriptorSetBuilder &&other) noexcept : m_device(other.m_device) { // TODO: Implement me! } -DescriptorSetUpdateBuilder &DescriptorSetUpdateBuilder::add_uniform_buffer_update(const VkDescriptorSet descriptor_set, - std::weak_ptr buffer) { +WriteDescriptorSetBuilder &WriteDescriptorSetBuilder::add_uniform_buffer_update(const VkDescriptorSet descriptor_set, + std::weak_ptr buffer) { + assert(descriptor_set); if (buffer.lock()->m_buffer_type != BufferType::UNIFORM_BUFFER) { throw std::invalid_argument("[DescriptorSetUpdateBuilder::add_uniform_buffer_update] Error: Buffer " + buffer.lock()->m_name + " is not a uniform buffer!"); } - m_write_sets.emplace_back(wrapper::make_info({ + m_write_descriptor_sets.emplace_back(wrapper::make_info({ .dstSet = descriptor_set, .dstBinding = m_binding, .dstArrayElement = 0, @@ -30,15 +31,15 @@ DescriptorSetUpdateBuilder &DescriptorSetUpdateBuilder::add_uniform_buffer_updat .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .pBufferInfo = &buffer.lock()->m_descriptor_buffer_info, })); - m_binding++; return *this; } -DescriptorSetUpdateBuilder & -DescriptorSetUpdateBuilder::add_combined_image_sampler_update(const VkDescriptorSet descriptor_set, - std::weak_ptr texture) { - m_write_sets.emplace_back(wrapper::make_info({ +WriteDescriptorSetBuilder & +WriteDescriptorSetBuilder::add_combined_image_sampler_update(const VkDescriptorSet descriptor_set, + std::weak_ptr texture) { + assert(descriptor_set); + m_write_descriptor_sets.emplace_back(wrapper::make_info({ .dstSet = descriptor_set, .dstBinding = m_binding, .dstArrayElement = 0, @@ -46,15 +47,18 @@ DescriptorSetUpdateBuilder::add_combined_image_sampler_update(const VkDescriptor .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .pImageInfo = &texture.lock()->m_descriptor_img_info, })); - m_binding++; return *this; } -void DescriptorSetUpdateBuilder::update() { - vkUpdateDescriptorSets(m_device.device(), static_cast(m_write_sets.size()), m_write_sets.data(), 0, - nullptr); - m_write_sets.clear(); +std::vector WriteDescriptorSetBuilder::build() { + auto write_descriptor_sets = std::move(m_write_descriptor_sets); + reset(); + return write_descriptor_sets; +} + +void WriteDescriptorSetBuilder::reset() { + m_write_descriptor_sets.clear(); m_binding = 0; }