From f9ec49e20e84dc1a73b64460bbf2d8fab4de71a0 Mon Sep 17 00:00:00 2001
From: Artur Wasmut <awasmut@uni-koblenz.de>
Date: Sun, 16 May 2021 14:13:55 +0200
Subject: [PATCH] add pass manager.

---
 config/Sources.cmake                          |   7 +-
 include/vkcv/Core.hpp                         |  16 +-
 include/vkcv/Handles.hpp                      |   2 +-
 .../vkcv/{Renderpass.hpp => PassConfig.hpp}   |   5 +-
 include/vkcv/Pipeline.hpp                     |   4 +-
 projects/first_triangle/src/main.cpp          |   9 +-
 src/vkcv/Core.cpp                             | 140 ++----------------
 src/vkcv/{Renderpass.cpp => PassConfig.cpp}   |   8 +-
 src/vkcv/PassManager.cpp                      | 139 +++++++++++++++++
 src/vkcv/PassManager.hpp                      |  32 ++++
 src/vkcv/Pipeline.cpp                         |   2 +-
 11 files changed, 214 insertions(+), 150 deletions(-)
 rename include/vkcv/{Renderpass.hpp => PassConfig.hpp} (88%)
 rename src/vkcv/{Renderpass.cpp => PassConfig.cpp} (75%)
 create mode 100644 src/vkcv/PassManager.cpp
 create mode 100644 src/vkcv/PassManager.hpp

diff --git a/config/Sources.cmake b/config/Sources.cmake
index 1814f581..0a612e7f 100644
--- a/config/Sources.cmake
+++ b/config/Sources.cmake
@@ -7,8 +7,11 @@ set(vkcv_sources
 		${vkcv_include}/vkcv/Core.hpp
 		${vkcv_source}/vkcv/Core.cpp
 
-		${vkcv_include}/vkcv/Renderpass.hpp
-		${vkcv_source}/vkcv/Renderpass.cpp
+		${vkcv_include}/vkcv/PassConfig.hpp
+		${vkcv_source}/vkcv/PassConfig.cpp
+
+		${vkcv_source}/vkcv/PassManager.hpp
+		${vkcv_source}/vkcv/PassManager.cpp
 
 		${vkcv_include}/vkcv/Handles.hpp
 		${vkcv_source}/vkcv/Handles.cpp
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index f27ca4cc..048b1ad5 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -4,11 +4,13 @@
  * @brief Handling of global states regarding dependencies
  */
 
+#include <memory>
+
 #include <vulkan/vulkan.hpp>
 #include "vkcv/Context.hpp"
 #include "vkcv/SwapChain.hpp"
 #include "vkcv/Window.hpp"
-#include "vkcv/Renderpass.hpp"
+#include "vkcv/PassConfig.hpp"
 #include "vkcv/Handles.hpp"
 #include "vkcv/Pipeline.hpp"
 
@@ -16,7 +18,9 @@ namespace vkcv
 {
     // TODO:
     class Buffer;
-    class Renderpass;
+
+    // forward declarations
+    class PassManager;
 
     class Core final
     {
@@ -41,11 +45,7 @@ namespace vkcv
         std::vector<vk::Pipeline> m_Pipelines;
         std::vector<vk::PipelineLayout> m_PipelineLayouts;
 
-        uint64_t m_NextRenderpassId;
-
-        uint64_t m_NextPassId;
-        std::vector<vk::RenderPass> m_Renderpasses;
-
+        std::unique_ptr<PassManager> m_PassManager;
     public:
         /**
          * Destructor of #Core destroys the Vulkan objects contained in the core's context.
@@ -121,6 +121,6 @@ namespace vkcv
         BufferHandle createBuffer(const Buffer &buf);
 
         [[nodiscard]]
-        bool createRenderpass(const Renderpass &pass, RenderpassHandle &handle);
+        PassHandle createPass(const PassConfig &config);
     };
 }
diff --git a/include/vkcv/Handles.hpp b/include/vkcv/Handles.hpp
index 52bc5dce..4ec2bc05 100644
--- a/include/vkcv/Handles.hpp
+++ b/include/vkcv/Handles.hpp
@@ -11,6 +11,6 @@ namespace vkcv
 {
     // Handle returned for any buffer created with the core/context objects
     struct BufferHandle     {uint64_t id;};
-    struct RenderpassHandle {uint64_t id;};
+    struct PassHandle       {uint64_t id;};
     struct PipelineHandle   {uint64_t id;};
 }
diff --git a/include/vkcv/Renderpass.hpp b/include/vkcv/PassConfig.hpp
similarity index 88%
rename from include/vkcv/Renderpass.hpp
rename to include/vkcv/PassConfig.hpp
index 7f2ad91c..b0ded8f5 100644
--- a/include/vkcv/Renderpass.hpp
+++ b/include/vkcv/PassConfig.hpp
@@ -46,9 +46,10 @@ namespace vkcv
         AttachmentOperation load_operation;
     };
 
-    struct Renderpass
+    struct PassConfig
     {
-        Renderpass() noexcept = default;
+        PassConfig() = delete;
+        explicit PassConfig(std::vector<AttachmentDescription> attachments) noexcept;
         std::vector<AttachmentDescription> attachments{};
     };
 }
\ No newline at end of file
diff --git a/include/vkcv/Pipeline.hpp b/include/vkcv/Pipeline.hpp
index 83dd6e4f..06380d0a 100644
--- a/include/vkcv/Pipeline.hpp
+++ b/include/vkcv/Pipeline.hpp
@@ -31,12 +31,12 @@ namespace vkcv {
          * @param width width of the application window
          * @param passHandle handle for Render Pass
          */
-        Pipeline(const ShaderProgram& shaderProgram, uint32_t width, uint32_t height, RenderpassHandle &passHandle);
+        Pipeline(const ShaderProgram& shaderProgram, uint32_t width, uint32_t height, PassHandle &passHandle);
 
 		ShaderProgram m_shaderProgram;
         uint32_t m_height;
         uint32_t m_width;
-        RenderpassHandle m_passHandle;
+        PassHandle m_passHandle;
     };
 
 }
diff --git a/projects/first_triangle/src/main.cpp b/projects/first_triangle/src/main.cpp
index b1a68c7f..e39e19c3 100644
--- a/projects/first_triangle/src/main.cpp
+++ b/projects/first_triangle/src/main.cpp
@@ -48,11 +48,10 @@ int main(int argc, const char** argv) {
 		vkcv::AttachmentOperation::STORE,
 		vkcv::AttachmentOperation::CLEAR);
 
-	vkcv::Renderpass trianglePassDefinition;
-	trianglePassDefinition.attachments.push_back(present_color_attachment);
-	vkcv::RenderpassHandle trianglePass;
+	vkcv::PassConfig trianglePassDefinition({present_color_attachment});
+	vkcv::PassHandle trianglePass = core.createPass(trianglePassDefinition);
 
-	if (!core.createRenderpass(trianglePassDefinition, trianglePass))
+	if (trianglePass.id == 0)
 	{
 		std::cout << "Error. Could not create renderpass. Exiting." << std::endl;
 		return EXIT_FAILURE;
@@ -63,7 +62,7 @@ int main(int argc, const char** argv) {
 	triangleShaderProgram.addShader(vkcv::ShaderProgram::ShaderStage::FRAGMENT, "shaders/frag.spv");
 
 	const vkcv::Pipeline trianglePipelineDefinition(triangleShaderProgram, windowWidth, windowHeight, trianglePass);
-	vkcv::PipelineHandle trianglePipeline;
+	vkcv::PipelineHandle trianglePipeline{};
 	if (!core.createGraphicsPipeline(trianglePipelineDefinition, trianglePipeline)) {
 		std::cout << "Error. Could not create graphics pipeline. Exiting." << std::endl;
 		return EXIT_FAILURE;
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 166ac3e5..6015363c 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -5,53 +5,10 @@
  */
 
 #include "vkcv/Core.hpp"
+#include "PassManager.hpp"
 
 namespace vkcv
 {
-    static vk::ImageLayout getVkLayoutFromAttachLayout(AttachmentLayout layout)
-    {
-        switch(layout)
-        {
-            case AttachmentLayout::GENERAL:
-                return vk::ImageLayout::eGeneral;
-            case AttachmentLayout::COLOR_ATTACHMENT:
-                return vk::ImageLayout::eColorAttachmentOptimal;
-            case AttachmentLayout::SHADER_READ_ONLY:
-                return vk::ImageLayout::eShaderReadOnlyOptimal;
-            case AttachmentLayout::DEPTH_STENCIL_ATTACHMENT:
-                return vk::ImageLayout::eDepthStencilAttachmentOptimal;
-            case AttachmentLayout::DEPTH_STENCIL_READ_ONLY:
-                return vk::ImageLayout::eDepthStencilReadOnlyOptimal;
-            case AttachmentLayout::PRESENTATION:
-                return vk::ImageLayout::ePresentSrcKHR;
-            default:
-                return vk::ImageLayout::eUndefined;
-        }
-    }
-
-    static vk::AttachmentStoreOp getVkStoreOpFromAttachOp(AttachmentOperation op)
-    {
-        switch(op)
-        {
-            case AttachmentOperation::STORE:
-                return vk::AttachmentStoreOp::eStore;
-            default:
-                return vk::AttachmentStoreOp::eDontCare;
-        }
-    }
-
-    static vk::AttachmentLoadOp getVKLoadOpFromAttachOp(AttachmentOperation op)
-    {
-        switch(op)
-        {
-            case AttachmentOperation::LOAD:
-                return vk::AttachmentLoadOp::eLoad;
-            case AttachmentOperation::CLEAR:
-                return vk::AttachmentLoadOp::eClear;
-            default:
-                return vk::AttachmentLoadOp::eDontCare;
-        }
-    }
 
     /**
      * @brief The physical device is evaluated by three categories:
@@ -508,12 +465,10 @@ namespace vkcv
 			m_NextPipelineId(0),
 			m_Pipelines{},
 			m_PipelineLayouts{},
-			m_NextRenderpassId(0),
-			m_NextPassId(0),
-			m_Renderpasses{}
+			m_PassManager{std::make_unique<PassManager>(m_Context.m_Device)}
     {}
 
-	Core::~Core() {
+	Core::~Core() noexcept {
 		std::cout << " Core " << std::endl;
 
 		for(const auto &layout : m_PipelineLayouts)
@@ -531,19 +486,14 @@ namespace vkcv
 		m_NextPipelineId = 0;
 
 		for (auto image : m_swapchainImageViews) {
-			m_Context.getDevice().destroyImageView(image);
+			m_Context.m_Device.destroyImageView(image);
 		}
-		for (const auto& pass : m_Renderpasses)
-			m_Context.m_Device.destroy(pass);
 
-		m_Renderpasses.clear();
-		m_NextPassId = 0;
-
-		m_Context.getDevice().destroySwapchainKHR(m_swapchain.getSwapchain());
-		m_Context.getInstance().destroySurfaceKHR(m_swapchain.getSurface());
+		m_Context.m_Device.destroySwapchainKHR(m_swapchain.getSwapchain());
+		m_Context.m_Instance.destroySurfaceKHR(m_swapchain.getSurface());
 	}
 
-	bool Core::createGraphicsPipeline(const Pipeline& pipeline, PipelineHandle& handle) {
+	bool Core::createGraphicsPipeline(const Pipeline &pipeline, PipelineHandle &handle) {
 		
 		// TODO: this search could be avoided if ShaderProgram could be queried for a specific stage
 		const auto shaderStageFlags = pipeline.m_shaderProgram.getShaderStages();
@@ -559,8 +509,8 @@ namespace vkcv
 			}
 		}
 
-		const bool foundVertexCode = vertexCode.empty();
-		const bool foundFragCode = fragCode.empty();
+		const bool foundVertexCode = !vertexCode.empty();
+		const bool foundFragCode = !fragCode.empty();
 		const bool foundRequiredShaderCode = foundVertexCode && foundFragCode;
 		if (!foundRequiredShaderCode) {
 			std::cout << "Core::createGraphicsPipeline requires vertex and fragment shader code" << std::endl; 
@@ -705,7 +655,7 @@ namespace vkcv
 			&pipelineColorBlendStateCreateInfo,
 			nullptr,
 			vkPipelineLayout,
-			m_Renderpasses[pipeline.m_passHandle.id],
+			m_PassManager->getVkPass(pipeline.m_passHandle),
 			0,
 			{},
 			0
@@ -729,74 +679,8 @@ namespace vkcv
 		return true;
 	}
 
-    bool Core::createRenderpass(const Renderpass &pass, RenderpassHandle &handle)
+    PassHandle Core::createPass(const PassConfig &config)
     {
-        // description of all {color, input, depth/stencil} attachments of the render pass
-        std::vector<vk::AttachmentDescription> attachmentDescriptions{};
-
-        // individual references to color attachments (of a subpass)
-        std::vector<vk::AttachmentReference> colorAttachmentReferences{};
-        // individual reference to depth attachment (of a subpass)
-        vk::AttachmentReference depthAttachmentReference{};
-		vk::AttachmentReference *pDepthAttachment = nullptr;	//stays nullptr if no depth attachment used
-
-		for (uint32_t i = 0; i < pass.attachments.size(); i++)
-		{
-			// TODO: Renderpass struct should hold proper format information
-			vk::Format format;
-
-			if (pass.attachments[i].layout_in_pass == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT)
-			{
-				format = vk::Format::eD16Unorm; // depth attachments;
-
-				depthAttachmentReference.attachment = i;
-				depthAttachmentReference.layout = getVkLayoutFromAttachLayout(pass.attachments[i].layout_in_pass);
-				pDepthAttachment = &depthAttachmentReference;
-			}
-			else
-			{
-				format = vk::Format::eB8G8R8A8Srgb; // color attachments, compatible with swapchain
-				vk::AttachmentReference attachmentRef(i, getVkLayoutFromAttachLayout(pass.attachments[i].layout_in_pass));
-				colorAttachmentReferences.push_back(attachmentRef);
-			}
-
-			vk::AttachmentDescription attachmentDesc({},
-				format,
-				vk::SampleCountFlagBits::e1,
-				getVKLoadOpFromAttachOp(pass.attachments[i].load_operation),
-				getVkStoreOpFromAttachOp(pass.attachments[i].load_operation),
-				vk::AttachmentLoadOp::eDontCare,
-				vk::AttachmentStoreOp::eDontCare,
-				getVkLayoutFromAttachLayout(pass.attachments[i].layout_initial),
-				getVkLayoutFromAttachLayout(pass.attachments[i].layout_final));
-			attachmentDescriptions.push_back(attachmentDesc);
-		}
-        vk::SubpassDescription subpassDescription({},
-			vk::PipelineBindPoint::eGraphics,
-			0,
-			{},
-			static_cast<uint32_t>(colorAttachmentReferences.size()),
-			colorAttachmentReferences.data(),
-			{},
-			pDepthAttachment,
-			0,
-			{});
-
-        vk::RenderPassCreateInfo passInfo({},
-                                          static_cast<uint32_t>(attachmentDescriptions.size()),
-                                          attachmentDescriptions.data(),
-                                          1,
-                                          &subpassDescription,
-                                          0,
-                                          {});
-
-        vk::RenderPass vkObject{nullptr};
-        if(m_Context.m_Device.createRenderPass(&passInfo, nullptr, &vkObject) != vk::Result::eSuccess)
-            return false;
-
-        m_Renderpasses.push_back(vkObject);
-        handle.id = m_NextPassId++;
-
-        return true;
+        return m_PassManager->createPass(config);
     }
 }
diff --git a/src/vkcv/Renderpass.cpp b/src/vkcv/PassConfig.cpp
similarity index 75%
rename from src/vkcv/Renderpass.cpp
rename to src/vkcv/PassConfig.cpp
index e9f2459b..a0c22896 100644
--- a/src/vkcv/Renderpass.cpp
+++ b/src/vkcv/PassConfig.cpp
@@ -1,4 +1,6 @@
-#include "vkcv/Renderpass.hpp"
+#include "vkcv/PassConfig.hpp"
+
+#include <utility>
 
 namespace vkcv
 {
@@ -13,4 +15,8 @@ namespace vkcv
     store_operation{store_op},
     load_operation{load_op}
     {};
+
+    PassConfig::PassConfig(std::vector<AttachmentDescription> attachments) noexcept :
+    attachments{std::move(attachments)}
+    {}
 }
\ No newline at end of file
diff --git a/src/vkcv/PassManager.cpp b/src/vkcv/PassManager.cpp
new file mode 100644
index 00000000..43674620
--- /dev/null
+++ b/src/vkcv/PassManager.cpp
@@ -0,0 +1,139 @@
+#include "PassManager.hpp"
+
+namespace vkcv
+{
+    static vk::ImageLayout getVkLayoutFromAttachLayout(AttachmentLayout layout)
+    {
+        switch(layout)
+        {
+            case AttachmentLayout::GENERAL:
+                return vk::ImageLayout::eGeneral;
+            case AttachmentLayout::COLOR_ATTACHMENT:
+                return vk::ImageLayout::eColorAttachmentOptimal;
+            case AttachmentLayout::SHADER_READ_ONLY:
+                return vk::ImageLayout::eShaderReadOnlyOptimal;
+            case AttachmentLayout::DEPTH_STENCIL_ATTACHMENT:
+                return vk::ImageLayout::eDepthStencilAttachmentOptimal;
+            case AttachmentLayout::DEPTH_STENCIL_READ_ONLY:
+                return vk::ImageLayout::eDepthStencilReadOnlyOptimal;
+            case AttachmentLayout::PRESENTATION:
+                return vk::ImageLayout::ePresentSrcKHR;
+            default:
+                return vk::ImageLayout::eUndefined;
+        }
+    }
+
+    static vk::AttachmentStoreOp getVkStoreOpFromAttachOp(AttachmentOperation op)
+    {
+        switch(op)
+        {
+            case AttachmentOperation::STORE:
+                return vk::AttachmentStoreOp::eStore;
+            default:
+                return vk::AttachmentStoreOp::eDontCare;
+        }
+    }
+
+    static vk::AttachmentLoadOp getVKLoadOpFromAttachOp(AttachmentOperation op)
+    {
+        switch(op)
+        {
+            case AttachmentOperation::LOAD:
+                return vk::AttachmentLoadOp::eLoad;
+            case AttachmentOperation::CLEAR:
+                return vk::AttachmentLoadOp::eClear;
+            default:
+                return vk::AttachmentLoadOp::eDontCare;
+        }
+    }
+
+    PassManager::PassManager(vk::Device device) noexcept :
+    m_Device{device},
+    m_RenderPasses{},
+    m_NextPassId{1}
+    {}
+
+    PassManager::~PassManager() noexcept
+    {
+        for(const auto &pass : m_RenderPasses)
+        {
+            m_Device.destroy(pass);
+        }
+        m_RenderPasses.clear();
+        m_NextPassId = 1;
+    }
+
+    PassHandle PassManager::createPass(const PassConfig &config)
+    {
+        // description of all {color, input, depth/stencil} attachments of the render pass
+        std::vector<vk::AttachmentDescription> attachmentDescriptions{};
+
+        // individual references to color attachments (of a subpass)
+        std::vector<vk::AttachmentReference> colorAttachmentReferences{};
+        // individual reference to depth attachment (of a subpass)
+        vk::AttachmentReference depthAttachmentReference{};
+        vk::AttachmentReference *pDepthAttachment = nullptr;	//stays nullptr if no depth attachment used
+
+        for (uint32_t i = 0; i < config.attachments.size(); i++)
+        {
+            // TODO: Renderpass struct should hold proper format information
+            vk::Format format;
+
+            if (config.attachments[i].layout_in_pass == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT)
+            {
+                format = vk::Format::eD16Unorm; // depth attachments;
+
+                depthAttachmentReference.attachment = i;
+                depthAttachmentReference.layout = getVkLayoutFromAttachLayout(config.attachments[i].layout_in_pass);
+                pDepthAttachment = &depthAttachmentReference;
+            }
+            else
+            {
+                format = vk::Format::eB8G8R8A8Srgb; // color attachments, compatible with swapchain
+                vk::AttachmentReference attachmentRef(i, getVkLayoutFromAttachLayout(config.attachments[i].layout_in_pass));
+                colorAttachmentReferences.push_back(attachmentRef);
+            }
+
+            vk::AttachmentDescription attachmentDesc({},
+                                                     format,
+                                                     vk::SampleCountFlagBits::e1,
+                                                     getVKLoadOpFromAttachOp(config.attachments[i].load_operation),
+                                                     getVkStoreOpFromAttachOp(config.attachments[i].load_operation),
+                                                     vk::AttachmentLoadOp::eDontCare,
+                                                     vk::AttachmentStoreOp::eDontCare,
+                                                     getVkLayoutFromAttachLayout(config.attachments[i].layout_initial),
+                                                     getVkLayoutFromAttachLayout(config.attachments[i].layout_final));
+            attachmentDescriptions.push_back(attachmentDesc);
+        }
+        vk::SubpassDescription subpassDescription({},
+                                                  vk::PipelineBindPoint::eGraphics,
+                                                  0,
+                                                  {},
+                                                  static_cast<uint32_t>(colorAttachmentReferences.size()),
+                                                  colorAttachmentReferences.data(),
+                                                  {},
+                                                  pDepthAttachment,
+                                                  0,
+                                                  {});
+
+        vk::RenderPassCreateInfo passInfo({},
+                                          static_cast<uint32_t>(attachmentDescriptions.size()),
+                                          attachmentDescriptions.data(),
+                                          1,
+                                          &subpassDescription,
+                                          0,
+                                          {});
+
+        vk::RenderPass vkObject{nullptr};
+        if(m_Device.createRenderPass(&passInfo, nullptr, &vkObject) != vk::Result::eSuccess)
+            return PassHandle{0};
+
+        m_RenderPasses.push_back(vkObject);
+            return PassHandle{m_NextPassId++};
+    }
+
+    vk::RenderPass PassManager::getVkPass(const PassHandle &handle) const
+    {
+        return m_RenderPasses[handle.id - 1];
+    }
+}
diff --git a/src/vkcv/PassManager.hpp b/src/vkcv/PassManager.hpp
new file mode 100644
index 00000000..b6be2cb1
--- /dev/null
+++ b/src/vkcv/PassManager.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+#include <vulkan/vulkan.hpp>
+#include <vector>
+#include "vkcv/Handles.hpp"
+#include "vkcv/PassConfig.hpp"
+
+namespace vkcv
+{
+    class PassManager
+    {
+    private:
+        vk::Device m_Device;
+        std::vector<vk::RenderPass> m_RenderPasses;
+        uint64_t m_NextPassId;
+    public:
+        PassManager() = delete; // no default ctor
+        explicit PassManager(vk::Device device) noexcept; // ctor
+        ~PassManager() noexcept; // dtor
+
+        PassManager(const PassManager &other) = delete; // copy-ctor
+        PassManager(PassManager &&other) = delete; // move-ctor;
+
+        PassManager & operator=(const PassManager &other) = delete; // copy-assign op
+        PassManager & operator=(PassManager &&other) = delete; // move-assign op
+
+        PassHandle createPass(const PassConfig &config);
+
+        [[nodiscard]]
+        vk::RenderPass getVkPass(const PassHandle &handle) const;
+    };
+}
diff --git a/src/vkcv/Pipeline.cpp b/src/vkcv/Pipeline.cpp
index 42a6b963..df3e6041 100644
--- a/src/vkcv/Pipeline.cpp
+++ b/src/vkcv/Pipeline.cpp
@@ -8,6 +8,6 @@
 
 namespace vkcv {
 
-    Pipeline::Pipeline(const ShaderProgram& shaderProgram, uint32_t width, uint32_t height, RenderpassHandle &passHandle):
+    Pipeline::Pipeline(const ShaderProgram& shaderProgram, uint32_t width, uint32_t height, PassHandle &passHandle):
 		m_shaderProgram(shaderProgram), m_height(height), m_width(width), m_passHandle(passHandle) {}
 }
-- 
GitLab