Added reference counting for PipelineLayout objects

Bug: b/155664177
Change-Id: I9322be202434908dfd652980f50e1024cf2efa2d
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/45368
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
Tested-by: Trevor Black <vantablack@google.com>
Presubmit-Ready: Trevor Black <vantablack@google.com>
diff --git a/src/Vulkan/VkDestroy.hpp b/src/Vulkan/VkDestroy.hpp
index 3e0589c..4b057a1 100644
--- a/src/Vulkan/VkDestroy.hpp
+++ b/src/Vulkan/VkDestroy.hpp
@@ -65,4 +65,22 @@
 	}
 }
 
+template<typename VkT>
+inline void release(VkT vkObject, const VkAllocationCallbacks *pAllocator)
+{
+	auto object = Cast(vkObject);
+	if(object)
+	{
+		using T = typename std::remove_pointer<decltype(object)>::type;
+		if(object->release(pAllocator))
+		{
+			object->~T();
+			// object may not point to the same pointer as vkObject, for dispatchable objects,
+			// for example, so make sure to deallocate based on the vkObject pointer, which
+			// should always point to the beginning of the allocated memory
+			vk::deallocate(vkObject, pAllocator);
+		}
+	}
+}
+
 }  // namespace vk
diff --git a/src/Vulkan/VkPipeline.cpp b/src/Vulkan/VkPipeline.cpp
index 0de27b2..e0af236 100644
--- a/src/Vulkan/VkPipeline.cpp
+++ b/src/Vulkan/VkPipeline.cpp
@@ -14,6 +14,7 @@
 
 #include "VkPipeline.hpp"
 
+#include "VkDestroy.hpp"
 #include "VkDevice.hpp"
 #include "VkPipelineCache.hpp"
 #include "VkPipelineLayout.hpp"
@@ -137,11 +138,19 @@
 
 namespace vk {
 
-Pipeline::Pipeline(PipelineLayout const *layout, const Device *device)
+Pipeline::Pipeline(PipelineLayout *layout, const Device *device)
     : layout(layout)
     , device(device)
     , robustBufferAccess(device->getEnabledFeatures().robustBufferAccess)
 {
+	layout->incRefCount();
+}
+
+void Pipeline::destroy(const VkAllocationCallbacks *pAllocator)
+{
+	destroyPipeline(pAllocator);
+
+	vk::release(static_cast<VkPipelineLayout>(*layout), pAllocator);
 }
 
 GraphicsPipeline::GraphicsPipeline(const VkGraphicsPipelineCreateInfo *pCreateInfo, void *mem, const Device *device)
diff --git a/src/Vulkan/VkPipeline.hpp b/src/Vulkan/VkPipeline.hpp
index 0f2611d..ca2ecf2 100644
--- a/src/Vulkan/VkPipeline.hpp
+++ b/src/Vulkan/VkPipeline.hpp
@@ -42,7 +42,7 @@
 class Pipeline
 {
 public:
-	Pipeline(PipelineLayout const *layout, const Device *device);
+	Pipeline(PipelineLayout *layout, const Device *device);
 	virtual ~Pipeline() = default;
 
 	operator VkPipeline()
@@ -55,23 +55,20 @@
 		return vk::VkTtoT<Pipeline, VkPipeline>(object);
 	}
 
-	void destroy(const VkAllocationCallbacks *pAllocator)
-	{
-		destroyPipeline(pAllocator);
-	}
+	void destroy(const VkAllocationCallbacks *pAllocator);
 
 	virtual void destroyPipeline(const VkAllocationCallbacks *pAllocator) = 0;
 #ifndef NDEBUG
 	virtual VkPipelineBindPoint bindPoint() const = 0;
 #endif
 
-	PipelineLayout const *getLayout() const
+	PipelineLayout *getLayout() const
 	{
 		return layout;
 	}
 
 protected:
-	PipelineLayout const *layout = nullptr;
+	PipelineLayout *layout = nullptr;
 	Device const *const device;
 
 	const bool robustBufferAccess = true;
diff --git a/src/Vulkan/VkPipelineLayout.cpp b/src/Vulkan/VkPipelineLayout.cpp
index f01ed90..96a84a7 100644
--- a/src/Vulkan/VkPipelineLayout.cpp
+++ b/src/Vulkan/VkPipelineLayout.cpp
@@ -55,6 +55,8 @@
 	size_t pushConstantRangesSize = pCreateInfo->pushConstantRangeCount * sizeof(VkPushConstantRange);
 	pushConstantRanges = reinterpret_cast<VkPushConstantRange *>(bindingStorage);
 	memcpy(pushConstantRanges, pCreateInfo->pPushConstantRanges, pushConstantRangesSize);
+
+	incRefCount();
 }
 
 void PipelineLayout::destroy(const VkAllocationCallbacks *pAllocator)
@@ -62,6 +64,16 @@
 	vk::deallocate(descriptorSets[0].bindings, pAllocator);  // pushConstantRanges are in the same allocation
 }
 
+bool PipelineLayout::release(const VkAllocationCallbacks *pAllocator)
+{
+	if(decRefCount() == 0)
+	{
+		vk::deallocate(descriptorSets[0].bindings, pAllocator);  // pushConstantRanges are in the same allocation
+		return true;
+	}
+	return false;
+}
+
 size_t PipelineLayout::ComputeRequiredAllocationSize(const VkPipelineLayoutCreateInfo *pCreateInfo)
 {
 	uint32_t bindingsCount = 0;
@@ -107,4 +119,14 @@
 	return DescriptorSetLayout::IsDescriptorDynamic(getDescriptorType(setNumber, bindingNumber));
 }
 
+uint32_t PipelineLayout::incRefCount()
+{
+	return ++refCount;
+}
+
+uint32_t PipelineLayout::decRefCount()
+{
+	return --refCount;
+}
+
 }  // namespace vk
diff --git a/src/Vulkan/VkPipelineLayout.hpp b/src/Vulkan/VkPipelineLayout.hpp
index 1ce716c..6f68dc8 100644
--- a/src/Vulkan/VkPipelineLayout.hpp
+++ b/src/Vulkan/VkPipelineLayout.hpp
@@ -25,6 +25,7 @@
 public:
 	PipelineLayout(const VkPipelineLayoutCreateInfo *pCreateInfo, void *mem);
 	void destroy(const VkAllocationCallbacks *pAllocator);
+	bool release(const VkAllocationCallbacks *pAllocator);
 
 	static size_t ComputeRequiredAllocationSize(const VkPipelineLayoutCreateInfo *pCreateInfo);
 
@@ -41,6 +42,9 @@
 
 	const uint32_t identifier;
 
+	uint32_t incRefCount();
+	uint32_t decRefCount();
+
 private:
 	struct Binding
 	{
@@ -60,6 +64,8 @@
 	const uint32_t descriptorSetCount = 0;
 	const uint32_t pushConstantRangeCount = 0;
 	VkPushConstantRange *pushConstantRanges = nullptr;
+
+	std::atomic<uint32_t> refCount{ 0 };
 };
 
 static inline PipelineLayout *Cast(VkPipelineLayout object)
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 33facca..4b4247d 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -1909,7 +1909,7 @@
 	TRACE("(VkDevice device = %p, VkPipelineLayout pipelineLayout = %p, const VkAllocationCallbacks* pAllocator = %p)",
 	      device, static_cast<void *>(pipelineLayout), pAllocator);
 
-	vk::destroy(pipelineLayout, pAllocator);
+	vk::release(pipelineLayout, pAllocator);
 }
 
 VKAPI_ATTR VkResult VKAPI_CALL vkCreateSampler(VkDevice device, const VkSamplerCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSampler *pSampler)