From e637ccf84be322fdfd3dee6e60aa873a26b9c82f Mon Sep 17 00:00:00 2001
From: Tobias Frisch <tfrisch@uni-koblenz.de>
Date: Tue, 1 Jun 2021 17:27:22 +0200
Subject: [PATCH] [#62] Added magic to free resources if unused and out of
 scope!

Signed-off-by: Tobias Frisch <tfrisch@uni-koblenz.de>
---
 include/vkcv/BufferManager.hpp | 16 ++++-----
 include/vkcv/Core.hpp          | 11 +++---
 include/vkcv/Handles.hpp       | 32 +++++++++++++----
 include/vkcv/Image.hpp         |  5 +--
 src/vkcv/BufferManager.cpp     |  8 ++---
 src/vkcv/Core.cpp              | 28 ++++++++-------
 src/vkcv/DescriptorManager.cpp | 26 ++++++++++----
 src/vkcv/DescriptorManager.hpp |  5 +--
 src/vkcv/Handles.cpp           | 66 +++++++++++++++++++++++++++++++---
 src/vkcv/Image.cpp             | 14 +++-----
 src/vkcv/ImageManager.cpp      | 46 ++++++++++++++++++++----
 src/vkcv/ImageManager.hpp      | 25 ++++++++-----
 src/vkcv/PassManager.cpp       | 27 +++++++++-----
 src/vkcv/PassManager.hpp       |  4 ++-
 src/vkcv/PipelineManager.cpp   | 63 ++++++++++++++++++++++----------
 src/vkcv/PipelineManager.hpp   | 12 +++++--
 src/vkcv/SamplerManager.cpp    |  8 ++---
 src/vkcv/SamplerManager.hpp    |  4 +--
 18 files changed, 288 insertions(+), 112 deletions(-)

diff --git a/include/vkcv/BufferManager.hpp b/include/vkcv/BufferManager.hpp
index 322873b3..a390a2ff 100644
--- a/include/vkcv/BufferManager.hpp
+++ b/include/vkcv/BufferManager.hpp
@@ -44,6 +44,14 @@ namespace vkcv
 		
 		void init();
 		
+		/**
+		 * Destroys and deallocates buffer represented by a given
+		 * buffer handle id.
+		 *
+		 * @param id Buffer handle id
+		 */
+		void destroyBufferById(uint64_t id);
+		
 	public:
 		~BufferManager() noexcept;
 		
@@ -123,14 +131,6 @@ namespace vkcv
 		 * @param handle Buffer handle
 		 */
 		void unmapBuffer(const BufferHandle& handle);
-	
-		/**
-		 * Destroys and deallocates buffer represented by a given
-		 * buffer handle.
-		 *
-		 * @param handle Buffer handle
-		 */
-		void destroyBuffer(const BufferHandle& handle);
 		
 	};
 	
diff --git a/include/vkcv/Core.hpp b/include/vkcv/Core.hpp
index cf75b8e7..ea9b5c77 100644
--- a/include/vkcv/Core.hpp
+++ b/include/vkcv/Core.hpp
@@ -21,6 +21,7 @@
 #include "vkcv/DescriptorConfig.hpp"
 #include "Sampler.hpp"
 #include "DescriptorWrites.hpp"
+#include "Event.hpp"
 
 namespace vkcv
 {
@@ -43,8 +44,8 @@ namespace vkcv
 		std::vector<vk::Semaphore> signalSemaphores;
 	};
 	
-	typedef std::function<void(const vk::CommandBuffer& cmdBuffer)> RecordCommandFunction;
-	typedef std::function<void(void)> FinishCommandFunction;
+	typedef typename event_function<const vk::CommandBuffer&>::type RecordCommandFunction;
+	typedef typename event_function<>::type FinishCommandFunction;
 
     class Core final
     {
@@ -80,6 +81,8 @@ namespace vkcv
 		SyncResources					m_SyncResources;
 		uint32_t						m_currentSwapchainImageIndex;
 		std::vector<vk::Framebuffer>	m_TemporaryFramebuffers;
+		
+		ImageHandle						m_DepthImage;
 
         /**
          * recreates the swapchain
@@ -229,8 +232,8 @@ namespace vkcv
 		void renderMesh(
 			const PassHandle						renderpassHandle, 
 			const PipelineHandle					pipelineHandle,
-			const int								width, 
-			const int								height, 
+			const uint32_t							width,
+			const uint32_t							height,
 			const size_t							pushConstantSize, 
 			const void*								pushConstantData, 
 			const std::vector<VertexBufferBinding>	&vertexBufferBindings, 
diff --git a/include/vkcv/Handles.hpp b/include/vkcv/Handles.hpp
index 58f795f0..86cd5555 100644
--- a/include/vkcv/Handles.hpp
+++ b/include/vkcv/Handles.hpp
@@ -7,31 +7,51 @@
 
 #include <iostream>
 
+#include "Event.hpp"
+
 namespace vkcv
 {
 	
+	typedef typename event_function<uint64_t>::type HandleDestroyFunction;
+	
 	class Handle {
 		friend std::ostream& operator << (std::ostream& out, const Handle& handle);
 		
 	private:
 		uint64_t m_id;
+		uint64_t* m_rc;
+		
+		HandleDestroyFunction m_destroy;
 	
 	protected:
 		Handle();
 		
-		explicit Handle(uint64_t id);
+		explicit Handle(uint64_t id, const HandleDestroyFunction& destroy = nullptr);
 		
+		/**
+		 * Returns the actual handle id of a handle.
+		 *
+		 * @return Handle id
+		 */
 		[[nodiscard]]
 		uint64_t getId() const;
+		
+		/**
+		 * Returns the reference counter of a handle
+		 *
+		 * @return Reference counter
+		 */
+		[[nodiscard]]
+		uint64_t getRC() const;
 	
 	public:
-		virtual ~Handle() = default;
+		virtual ~Handle();
 		
-		Handle(const Handle& other) = default;
-		Handle(Handle&& other) = default;
+		Handle(const Handle& other);
+		Handle(Handle&& other) noexcept;
 		
-		Handle& operator=(const Handle& other) = default;
-		Handle& operator=(Handle&& other) = default;
+		Handle& operator=(const Handle& other);
+		Handle& operator=(Handle&& other) noexcept;
 		
 		explicit operator bool() const;
 		bool operator!() const;
diff --git a/include/vkcv/Image.hpp b/include/vkcv/Image.hpp
index d56fac9c..d76bd121 100644
--- a/include/vkcv/Image.hpp
+++ b/include/vkcv/Image.hpp
@@ -39,12 +39,9 @@ namespace vkcv {
 		ImageManager* const m_manager;
 		const ImageHandle m_handle;
 		const vk::Format m_format;
-		const uint32_t m_width;
-		const uint32_t m_height;
-		const uint32_t m_depth;
 		vk::ImageLayout m_layout;
 
-		Image(ImageManager* manager, const ImageHandle& handle, vk::Format format, uint32_t width, uint32_t height, uint32_t depth);
+		Image(ImageManager* manager, const ImageHandle& handle, vk::Format format);
 		
 		static Image create(ImageManager* manager, vk::Format format, uint32_t width, uint32_t height, uint32_t depth);
 		
diff --git a/src/vkcv/BufferManager.cpp b/src/vkcv/BufferManager.cpp
index ab34a3df..ef874606 100644
--- a/src/vkcv/BufferManager.cpp
+++ b/src/vkcv/BufferManager.cpp
@@ -23,7 +23,7 @@ namespace vkcv {
 	
 	BufferManager::~BufferManager() noexcept {
 		for (uint64_t id = 0; id < m_buffers.size(); id++) {
-			destroyBuffer(BufferHandle(id));
+			destroyBufferById(id);
 		}
 	}
 	
@@ -119,7 +119,7 @@ namespace vkcv {
 		
 		const uint64_t id = m_buffers.size();
 		m_buffers.push_back({ buffer, memory, size, nullptr, mappable });
-		return BufferHandle{ id };
+		return BufferHandle(id, [&](uint64_t id) { destroyBufferById(id); });
 	}
 	
 	struct StagingStepInfo {
@@ -315,9 +315,7 @@ namespace vkcv {
 		buffer.m_mapped = nullptr;
 	}
 	
-	void BufferManager::destroyBuffer(const BufferHandle& handle) {
-		const uint64_t id = handle.getId();
-		
+	void BufferManager::destroyBufferById(uint64_t id) {
 		if (id >= m_buffers.size()) {
 			return;
 		}
diff --git a/src/vkcv/Core.cpp b/src/vkcv/Core.cpp
index 0b0d3a24..720b89c2 100644
--- a/src/vkcv/Core.cpp
+++ b/src/vkcv/Core.cpp
@@ -176,8 +176,8 @@ namespace vkcv
 	void Core::renderMesh(
 		const PassHandle						renderpassHandle, 
 		const PipelineHandle					pipelineHandle, 
-		const int								width, 
-		const int								height, 
+		const uint32_t 							width,
+		const uint32_t							height,
 		const size_t							pushConstantSize, 
 		const void								*pushConstantData,
 		const std::vector<VertexBufferBinding>& vertexBufferBindings, 
@@ -194,12 +194,18 @@ namespace vkcv
 		const vk::RenderPass renderpass = m_PassManager->getVkPass(renderpassHandle);
 		const PassConfig passConfig = m_PassManager->getPassConfig(renderpassHandle);
 		
-		ImageHandle depthImage;
+		const bool checkForDepthImage = (
+				(!m_DepthImage) ||
+				(width != m_ImageManager->getImageWidth(m_DepthImage)) ||
+				(height != m_ImageManager->getImageHeight(m_DepthImage))
+		);
 		
-		for (const auto& attachment : passConfig.attachments) {
-			if (attachment.layout_final == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT) {
-				depthImage = m_ImageManager->createImage(width, height, 1, attachment.format);
-				break;
+		if (checkForDepthImage) {
+			for (const auto &attachment : passConfig.attachments) {
+				if (attachment.layout_final == AttachmentLayout::DEPTH_STENCIL_ATTACHMENT) {
+					m_DepthImage = m_ImageManager->createImage(width, height, 1, attachment.format);
+					break;
+				}
 			}
 		}
 		
@@ -212,8 +218,8 @@ namespace vkcv
 		std::vector<vk::ImageView> attachments;
 		attachments.push_back(imageView);
 		
-		if (depthImage) {
-			attachments.push_back(m_ImageManager->getVulkanImageView(depthImage));
+		if (m_DepthImage) {
+			attachments.push_back(m_ImageManager->getVulkanImageView(m_DepthImage));
 		}
 		
 		const vk::Framebuffer framebuffer = createFramebuffer(
@@ -273,9 +279,7 @@ namespace vkcv
 			cmdBuffer.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eAll, 0, pushConstantSize, pushConstantData);
 			cmdBuffer.drawIndexed(indexCount, 1, 0, 0, {});
 			cmdBuffer.endRenderPass();
-		}, [&]() {
-			m_ImageManager->destroyImage(depthImage);
-		});
+		}, nullptr);
 	}
 
 	void Core::endFrame() {
diff --git a/src/vkcv/DescriptorManager.cpp b/src/vkcv/DescriptorManager.cpp
index cf689b44..5f1be539 100644
--- a/src/vkcv/DescriptorManager.cpp
+++ b/src/vkcv/DescriptorManager.cpp
@@ -8,7 +8,7 @@ namespace vkcv
     descriptorSetLayouts{std::move(layouts)}
     {}
     DescriptorManager::DescriptorManager(vk::Device device) noexcept:
-        m_Device{ device }, m_NextResourceDescriptionID{ 0 }
+        m_Device{ device }
     {
         /**
          * Allocate a set size for the initial pool, namely 1000 units of each descriptor type below.
@@ -32,11 +32,10 @@ namespace vkcv
 
     DescriptorManager::~DescriptorManager() noexcept
     {
-        for(const auto &resource : m_ResourceDescriptions)
-        {
-            for(const auto &layout : resource.descriptorSetLayouts)
-                m_Device.destroyDescriptorSetLayout(layout);
+        for (uint64_t id = 0; id < m_ResourceDescriptions.size(); id++) {
+			destroyResourceDescriptionById(id);
         }
+        
         m_Device.destroy(m_Pool);
     }
 
@@ -82,8 +81,9 @@ namespace vkcv
             return ResourcesHandle();
         };
 
+        const uint64_t id = m_ResourceDescriptions.size();
         m_ResourceDescriptions.emplace_back(vk_sets, vk_setLayouts);
-        return ResourcesHandle(m_NextResourceDescriptionID++);
+        return ResourcesHandle(id, [&](uint64_t id) { destroyResourceDescriptionById(id); });
     }
     
     struct WriteDescriptorSetInfo {
@@ -270,5 +270,19 @@ namespace vkcv
                 return vk::ShaderStageFlagBits::eAll;
         }
     }
+    
+    void DescriptorManager::destroyResourceDescriptionById(uint64_t id) {
+		if (id >= m_ResourceDescriptions.size()) {
+			return;
+		}
+		
+		auto& resourceDescription = m_ResourceDescriptions[id];
+	
+		for(const auto &layout : resourceDescription.descriptorSetLayouts) {
+			m_Device.destroyDescriptorSetLayout(layout);
+		}
+	
+		resourceDescription.descriptorSetLayouts.clear();
+	}
 
 }
\ No newline at end of file
diff --git a/src/vkcv/DescriptorManager.hpp b/src/vkcv/DescriptorManager.hpp
index 937fb278..8063277d 100644
--- a/src/vkcv/DescriptorManager.hpp
+++ b/src/vkcv/DescriptorManager.hpp
@@ -58,8 +58,6 @@ namespace vkcv
 		* Contains all the resource descriptions that were requested by the user in calls of createResourceDescription.
 		*/
         std::vector<ResourceDescription> m_ResourceDescriptions;
-        // Counter for the vector above
-        uint64_t m_NextResourceDescriptionID;
 		
 		/**
 		* Converts the flags of the descriptor types from VulkanCV (vkcv) to Vulkan (vk).
@@ -73,5 +71,8 @@ namespace vkcv
 		* @return vk flag of the ShaderStage
 		*/
 		static vk::ShaderStageFlagBits convertShaderStageFlag(ShaderStage stage);
+		
+		void destroyResourceDescriptionById(uint64_t id);
+		
 	};
 }
\ No newline at end of file
diff --git a/src/vkcv/Handles.cpp b/src/vkcv/Handles.cpp
index bd465d4e..4a00121f 100644
--- a/src/vkcv/Handles.cpp
+++ b/src/vkcv/Handles.cpp
@@ -3,17 +3,75 @@
 namespace vkcv {
 	
 	Handle::Handle() :
-		m_id(UINT64_MAX)
+	m_id(UINT64_MAX), m_rc(nullptr), m_destroy(nullptr)
 	{}
 	
-	Handle::Handle(uint64_t id) :
-		m_id(id)
+	Handle::Handle(uint64_t id, const HandleDestroyFunction& destroy) :
+		m_id(id), m_rc(new uint64_t(1)), m_destroy(destroy)
 	{}
 	
+	Handle::~Handle() {
+		if ((m_rc) && (--(*m_rc) == 0)) {
+			if (m_destroy) {
+				m_destroy(m_id);
+			}
+			
+			delete m_rc;
+		}
+	}
+	
+	Handle::Handle(const Handle &other) :
+		m_id(other.m_id),
+		m_rc(other.m_rc),
+		m_destroy(other.m_destroy)
+	{
+		if (m_rc) {
+			++(*m_rc);
+		}
+	}
+	
+	Handle::Handle(Handle &&other) noexcept :
+		m_id(other.m_id),
+		m_rc(other.m_rc),
+		m_destroy(other.m_destroy)
+	{
+		other.m_rc = nullptr;
+	}
+	
+	Handle &Handle::operator=(const Handle &other) {
+		if (&other == this) {
+			return *this;
+		}
+		
+		m_id = other.m_id;
+		m_rc = other.m_rc;
+		m_destroy = other.m_destroy;
+		
+		if (m_rc) {
+			++(*m_rc);
+		}
+		
+		return *this;
+	}
+	
+	Handle &Handle::operator=(Handle &&other) noexcept {
+		m_id = other.m_id;
+		m_rc = other.m_rc;
+		m_destroy = other.m_destroy;
+		
+		other.m_rc = nullptr;
+		
+		return *this;
+	}
+	
 	uint64_t Handle::getId() const {
 		return m_id;
 	}
 	
+	uint64_t Handle::getRC() const {
+		return m_rc? *m_rc : 0;
+	}
+	
 	Handle::operator bool() const {
 		return (m_id < UINT64_MAX);
 	}
@@ -24,7 +82,7 @@ namespace vkcv {
 	
 	std::ostream& operator << (std::ostream& out, const Handle& handle) {
 		if (handle) {
-			return out << "[Handle: " << handle.getId() << "]";
+			return out << "[Handle: " << handle.getId() << ":" << handle.getRC() << "]";
 		} else {
 			return out << "[Handle: none]";
 		}
diff --git a/src/vkcv/Image.cpp b/src/vkcv/Image.cpp
index 7104f784..9ce5c25a 100644
--- a/src/vkcv/Image.cpp
+++ b/src/vkcv/Image.cpp
@@ -10,7 +10,7 @@ namespace vkcv{
 	
 	Image Image::create(ImageManager* manager, vk::Format format, uint32_t width, uint32_t height, uint32_t depth)
 	{
-		return Image(manager, manager->createImage(width, height, depth, format), format, width, height, depth);
+		return Image(manager, manager->createImage(width, height, depth, format), format);
 	}
 	
 	vk::Format Image::getFormat() const {
@@ -18,15 +18,15 @@ namespace vkcv{
 	}
 	
 	uint32_t Image::getWidth() const {
-		return m_width;
+		return m_manager->getImageWidth(m_handle);
 	}
 	
 	uint32_t Image::getHeight() const {
-		return m_height;
+		return m_manager->getImageHeight(m_handle);
 	}
 	
 	uint32_t Image::getDepth() const {
-		return m_depth;
+		return m_manager->getImageDepth(m_handle);
 	}
 	
 	vk::ImageLayout Image::getLayout() const {
@@ -47,14 +47,10 @@ namespace vkcv{
 		m_manager->fillImage(m_handle, data, size);
 	}
 	
-	Image::Image(ImageManager* manager, const ImageHandle& handle,
-			  	 vk::Format format, uint32_t width, uint32_t height, uint32_t depth) :
+	Image::Image(ImageManager* manager, const ImageHandle& handle, vk::Format format) :
 		m_manager(manager),
 		m_handle(handle),
 		m_format(format),
-		m_width(width),
-		m_height(height),
-		m_depth(depth),
 		m_layout(vk::ImageLayout::eUndefined)
 	{
 	}
diff --git a/src/vkcv/ImageManager.cpp b/src/vkcv/ImageManager.cpp
index 3896d6bc..d3eb9cc5 100644
--- a/src/vkcv/ImageManager.cpp
+++ b/src/vkcv/ImageManager.cpp
@@ -43,7 +43,7 @@ namespace vkcv {
 
 	ImageManager::~ImageManager() noexcept {
 		for (uint64_t id = 0; id < m_images.size(); id++) {
-			destroyImage(ImageHandle(id));
+			destroyImageById(id);
 		}
 	}
 	
@@ -167,7 +167,7 @@ namespace vkcv {
 		
 		const uint64_t id = m_images.size();
 		m_images.push_back({ image, memory, view, width, height, depth, format, arrayLayers, mipLevels });
-		return ImageHandle(id);
+		return ImageHandle(id, [&](uint64_t id) { destroyImageById(id); });
 	}
 	
 	vk::Image ImageManager::getVulkanImage(const ImageHandle &handle) const {
@@ -359,16 +359,48 @@ namespace vkcv {
 							vk::ImageLayout::eTransferDstOptimal,
 							vk::ImageLayout::eShaderReadOnlyOptimal
 					);
-					
-					m_bufferManager.destroyBuffer(bufferHandle);
 				}
 		);
 	}
-
-	void ImageManager::destroyImage(const ImageHandle& handle)
-	{
+	
+	uint32_t ImageManager::getImageWidth(const ImageHandle &handle) const {
+		const uint64_t id = handle.getId();
+		
+		if (id >= m_images.size()) {
+			return 0;
+		}
+		
+		auto& image = m_images[id];
+		
+		return image.m_width;
+	}
+	
+	uint32_t ImageManager::getImageHeight(const ImageHandle &handle) const {
+		const uint64_t id = handle.getId();
+		
+		if (id >= m_images.size()) {
+			return 0;
+		}
+		
+		auto& image = m_images[id];
+		
+		return image.m_height;
+	}
+	
+	uint32_t ImageManager::getImageDepth(const ImageHandle &handle) const {
 		const uint64_t id = handle.getId();
 		
+		if (id >= m_images.size()) {
+			return 0;
+		}
+		
+		auto& image = m_images[id];
+		
+		return image.m_depth;
+	}
+	
+	void ImageManager::destroyImageById(uint64_t id)
+	{
 		if (id >= m_images.size()) {
 			return;
 		}
diff --git a/src/vkcv/ImageManager.hpp b/src/vkcv/ImageManager.hpp
index 7dc6746f..4b6e5208 100644
--- a/src/vkcv/ImageManager.hpp
+++ b/src/vkcv/ImageManager.hpp
@@ -36,6 +36,14 @@ namespace vkcv {
 		
 		ImageManager(BufferManager& bufferManager) noexcept;
 		
+		/**
+		 * Destroys and deallocates image represented by a given
+		 * image handle id.
+		 *
+		 * @param id Image handle id
+		 */
+		void destroyImageById(uint64_t id);
+		
 	public:
 		~ImageManager() noexcept;
 		ImageManager(ImageManager&& other) = delete;
@@ -57,14 +65,15 @@ namespace vkcv {
 		
 		void switchImageLayout(const ImageHandle& handle, vk::ImageLayout oldLayout, vk::ImageLayout newLayout);
 		void fillImage(const ImageHandle& handle, void* data, size_t size);
-
-		/**
-		 * Destroys and deallocates image represented by a given
-		 * buffer handle.
-		 *
-		 * @param handle Image handle
-		 */
-		void destroyImage(const ImageHandle& handle);
+		
+		[[nodiscard]]
+		uint32_t getImageWidth(const ImageHandle& handle) const;
+		
+		[[nodiscard]]
+		uint32_t getImageHeight(const ImageHandle& handle) const;
+		
+		[[nodiscard]]
+		uint32_t getImageDepth(const ImageHandle& handle) const;
 		
 	};
 }
\ No newline at end of file
diff --git a/src/vkcv/PassManager.cpp b/src/vkcv/PassManager.cpp
index 26e5f290..8b59495a 100644
--- a/src/vkcv/PassManager.cpp
+++ b/src/vkcv/PassManager.cpp
@@ -49,17 +49,14 @@ namespace vkcv
 
     PassManager::PassManager(vk::Device device) noexcept :
     m_Device{device},
-    m_Passes{},
-    m_NextPassId(0)
+    m_Passes{}
     {}
 
     PassManager::~PassManager() noexcept
     {
-        for(const auto &pass : m_Passes)
-            m_Device.destroy(pass.m_Handle);
-	
-		m_Passes.clear();
-        m_NextPassId = 0;
+    	for (uint64_t id = 0; id < m_Passes.size(); id++) {
+			destroyPassById(id);
+    	}
     }
 
     PassHandle PassManager::createPass(const PassConfig &config)
@@ -130,8 +127,9 @@ namespace vkcv
 
         vk::RenderPass renderPass = m_Device.createRenderPass(passInfo);
 	
+        const uint64_t id = m_Passes.size();
 		m_Passes.push_back({ renderPass, config });
-		return PassHandle(m_NextPassId++);
+		return PassHandle(id, [&](uint64_t id) { destroyPassById(id); });
     }
 
     vk::RenderPass PassManager::getVkPass(const PassHandle &handle) const
@@ -160,4 +158,17 @@ namespace vkcv
 		return pass.m_Config;
     }
     
+    void PassManager::destroyPassById(uint64_t id) {
+    	if (id >= m_Passes.size()) {
+    		return;
+    	}
+    	
+    	auto& pass = m_Passes[id];
+	
+		if (pass.m_Handle) {
+			m_Device.destroy(pass.m_Handle);
+			pass.m_Handle = nullptr;
+		}
+    }
+    
 }
diff --git a/src/vkcv/PassManager.hpp b/src/vkcv/PassManager.hpp
index bfc20fe2..661a8b27 100644
--- a/src/vkcv/PassManager.hpp
+++ b/src/vkcv/PassManager.hpp
@@ -17,7 +17,9 @@ namespace vkcv
     	
         vk::Device m_Device;
         std::vector<Pass> m_Passes;
-        uint64_t m_NextPassId;
+        
+        void destroyPassById(uint64_t id);
+        
     public:
         PassManager() = delete; // no default ctor
         explicit PassManager(vk::Device device) noexcept; // ctor
diff --git a/src/vkcv/PipelineManager.cpp b/src/vkcv/PipelineManager.cpp
index 1b0d665e..1b866c05 100644
--- a/src/vkcv/PipelineManager.cpp
+++ b/src/vkcv/PipelineManager.cpp
@@ -5,22 +5,14 @@ namespace vkcv
 
     PipelineManager::PipelineManager(vk::Device device) noexcept :
     m_Device{device},
-    m_Pipelines{},
-    m_PipelineLayouts{},
-    m_NextPipelineId{0}
+    m_Pipelines{}
     {}
 
     PipelineManager::~PipelineManager() noexcept
     {
-        for(const auto &pipeline: m_Pipelines)
-            m_Device.destroy(pipeline);
-
-        for(const auto &layout : m_PipelineLayouts)
-            m_Device.destroy(layout);
-
-        m_Pipelines.clear();
-        m_PipelineLayouts.clear();
-        m_NextPipelineId = 0;
+    	for (uint64_t id = 0; id < m_Pipelines.size(); id++) {
+			destroyPipelineById(id);
+    	}
     }
 
 	// currently assuming default 32 bit formats, no lower precision or normalized variants supported
@@ -252,19 +244,54 @@ namespace vkcv
 
         m_Device.destroy(vertexModule);
         m_Device.destroy(fragmentModule);
-
-        m_Pipelines.push_back(vkPipeline);
-        m_PipelineLayouts.push_back(vkPipelineLayout);
-        return PipelineHandle(m_NextPipelineId++);
+        
+        const uint64_t id = m_Pipelines.size();
+        m_Pipelines.push_back({ vkPipeline, vkPipelineLayout });
+        return PipelineHandle(id, [&](uint64_t id) { destroyPipelineById(id); });
     }
 
     vk::Pipeline PipelineManager::getVkPipeline(const PipelineHandle &handle) const
     {
-        return m_Pipelines.at(handle.getId());
+		const uint64_t id = handle.getId();
+	
+		if (id >= m_Pipelines.size()) {
+			return nullptr;
+		}
+	
+		auto& pipeline = m_Pipelines[id];
+	
+		return pipeline.m_handle;
     }
 
     vk::PipelineLayout PipelineManager::getVkPipelineLayout(const PipelineHandle &handle) const
     {
-        return m_PipelineLayouts.at(handle.getId());
+    	const uint64_t id = handle.getId();
+    	
+		if (id >= m_Pipelines.size()) {
+			return nullptr;
+		}
+	
+		auto& pipeline = m_Pipelines[id];
+    	
+        return pipeline.m_layout;
+    }
+    
+    void PipelineManager::destroyPipelineById(uint64_t id) {
+    	if (id >= m_Pipelines.size()) {
+    		return;
+    	}
+    	
+    	auto& pipeline = m_Pipelines[id];
+    	
+    	if (pipeline.m_handle) {
+			m_Device.destroy(pipeline.m_handle);
+			pipeline.m_handle = nullptr;
+    	}
+	
+		if (pipeline.m_layout) {
+			m_Device.destroy(pipeline.m_layout);
+			pipeline.m_layout = nullptr;
+		}
     }
+    
 }
\ No newline at end of file
diff --git a/src/vkcv/PipelineManager.hpp b/src/vkcv/PipelineManager.hpp
index 896d0df1..950df0be 100644
--- a/src/vkcv/PipelineManager.hpp
+++ b/src/vkcv/PipelineManager.hpp
@@ -11,10 +11,16 @@ namespace vkcv
     class PipelineManager
     {
     private:
+    	struct Pipeline {
+			vk::Pipeline m_handle;
+			vk::PipelineLayout m_layout;
+    	};
+    	
         vk::Device m_Device;
-        std::vector<vk::Pipeline> m_Pipelines;
-        std::vector<vk::PipelineLayout> m_PipelineLayouts;
-        uint64_t m_NextPipelineId;
+        std::vector<Pipeline> m_Pipelines;
+        
+        void destroyPipelineById(uint64_t id);
+        
     public:
         PipelineManager() = delete; // no default ctor
         explicit PipelineManager(vk::Device device) noexcept; // ctor
diff --git a/src/vkcv/SamplerManager.cpp b/src/vkcv/SamplerManager.cpp
index 7935bbc1..eb44356f 100644
--- a/src/vkcv/SamplerManager.cpp
+++ b/src/vkcv/SamplerManager.cpp
@@ -10,7 +10,7 @@ namespace vkcv {
 	
 	SamplerManager::~SamplerManager() {
 		for (uint64_t id = 0; id < m_samplers.size(); id++) {
-			destroySampler(SamplerHandle(id));
+			destroySamplerById(id);
 		}
 	}
 	
@@ -96,7 +96,7 @@ namespace vkcv {
 		
 		const uint64_t id = m_samplers.size();
 		m_samplers.push_back(sampler);
-		return SamplerHandle(id);
+		return SamplerHandle(id, [&](uint64_t id) { destroySamplerById(id); });
 	}
 	
 	vk::Sampler SamplerManager::getVulkanSampler(const SamplerHandle &handle) const {
@@ -109,9 +109,7 @@ namespace vkcv {
 		return m_samplers[id];
 	}
 	
-	void SamplerManager::destroySampler(const SamplerHandle &handle) {
-		const uint64_t id = handle.getId();
-		
+	void SamplerManager::destroySamplerById(uint64_t id) {
 		if (id >= m_samplers.size()) {
 			return;
 		}
diff --git a/src/vkcv/SamplerManager.hpp b/src/vkcv/SamplerManager.hpp
index 41f58b2f..511176d4 100644
--- a/src/vkcv/SamplerManager.hpp
+++ b/src/vkcv/SamplerManager.hpp
@@ -18,6 +18,8 @@ namespace vkcv {
 		
 		explicit SamplerManager(const vk::Device& device) noexcept;
 		
+		void destroySamplerById(uint64_t id);
+		
 	public:
 		~SamplerManager();
 		
@@ -34,8 +36,6 @@ namespace vkcv {
 		
 		[[nodiscard]]
 		vk::Sampler getVulkanSampler(const SamplerHandle& handle) const;
-		
-		void destroySampler(const SamplerHandle& handle);
 	
 	};
 	
-- 
GitLab