2
0
Panagiotis Christopoulos Charitos 1 жил өмнө
parent
commit
3b54294f63
34 өөрчлөгдсөн 625 нэмэгдсэн , 953 устгасан
  1. 9 9
      AnKi/Gr/D3D/D3DFence.cpp
  2. 1 1
      AnKi/Gr/D3D/D3DFence.h
  3. 1 0
      AnKi/Gr/Vulkan/VkAccelerationStructure.cpp
  4. 10 10
      AnKi/Gr/Vulkan/VkBuffer.cpp
  5. 1 1
      AnKi/Gr/Vulkan/VkCommandBuffer.cpp
  6. 4 4
      AnKi/Gr/Vulkan/VkCommandBufferFactory.cpp
  7. 12 15
      AnKi/Gr/Vulkan/VkCommandBufferFactory.h
  8. 0 144
      AnKi/Gr/Vulkan/VkDeferredBarrierFactory.h
  9. 38 8
      AnKi/Gr/Vulkan/VkFenceFactory.cpp
  10. 48 17
      AnKi/Gr/Vulkan/VkFenceFactory.h
  11. 0 66
      AnKi/Gr/Vulkan/VkFenceFactory.inl.h
  12. 2 2
      AnKi/Gr/Vulkan/VkFrameGarbageCollector.cpp
  13. 0 8
      AnKi/Gr/Vulkan/VkGpuMemoryManager.cpp
  14. 13 7
      AnKi/Gr/Vulkan/VkGpuMemoryManager.h
  15. 31 48
      AnKi/Gr/Vulkan/VkGrManager.cpp
  16. 1 95
      AnKi/Gr/Vulkan/VkGrManager.h
  17. 2 2
      AnKi/Gr/Vulkan/VkOcclusionQuery.cpp
  18. 129 5
      AnKi/Gr/Vulkan/VkPipeline.cpp
  19. 22 15
      AnKi/Gr/Vulkan/VkPipeline.h
  20. 0 123
      AnKi/Gr/Vulkan/VkPipelineCache.cpp
  21. 0 33
      AnKi/Gr/Vulkan/VkPipelineCache.h
  22. 2 2
      AnKi/Gr/Vulkan/VkPipelineQuery.cpp
  23. 30 7
      AnKi/Gr/Vulkan/VkQueryFactory.h
  24. 157 1
      AnKi/Gr/Vulkan/VkSampler.cpp
  25. 77 1
      AnKi/Gr/Vulkan/VkSampler.h
  26. 0 167
      AnKi/Gr/Vulkan/VkSamplerFactory.cpp
  27. 0 95
      AnKi/Gr/Vulkan/VkSamplerFactory.h
  28. 9 11
      AnKi/Gr/Vulkan/VkSemaphoreFactory.cpp
  29. 3 9
      AnKi/Gr/Vulkan/VkSemaphoreFactory.h
  30. 2 8
      AnKi/Gr/Vulkan/VkShaderProgram.cpp
  31. 5 7
      AnKi/Gr/Vulkan/VkSwapchainFactory.cpp
  32. 6 23
      AnKi/Gr/Vulkan/VkSwapchainFactory.h
  33. 8 7
      AnKi/Gr/Vulkan/VkTexture.cpp
  34. 2 2
      AnKi/Gr/Vulkan/VkTimestampQuery.cpp

+ 9 - 9
AnKi/Gr/D3D/D3DFence.cpp

@@ -63,6 +63,14 @@ void MicroFencePtrDeleter::operator()(MicroFence* f)
 	FenceFactory::getSingleton().deleteFence(f);
 }
 
+FenceFactory::~FenceFactory()
+{
+	trimSignaledFences(true);
+
+	ANKI_ASSERT(m_fences.getSize() == 0);
+	ANKI_ASSERT(m_aliveFenceCount == 0);
+}
+
 MicroFence* FenceFactory::newFence()
 {
 	MicroFence* out = nullptr;
@@ -114,7 +122,7 @@ MicroFence* FenceFactory::newFence()
 	return out;
 }
 
-void FenceFactory::trimAliveFences(Bool wait)
+void FenceFactory::trimSignaledFences(Bool wait)
 {
 	LockGuard<Mutex> lock(m_mtx);
 
@@ -149,14 +157,6 @@ void FenceFactory::trimAliveFences(Bool wait)
 	}
 }
 
-FenceFactory::~FenceFactory()
-{
-	trimAliveFences(true);
-
-	ANKI_ASSERT(m_fences.getSize() == 0);
-	ANKI_ASSERT(m_aliveFenceCount == 0);
-}
-
 void FenceFactory::deleteFence(MicroFence* fence)
 {
 	ANKI_ASSERT(fence);

+ 1 - 1
AnKi/Gr/D3D/D3DFence.h

@@ -90,7 +90,7 @@ public:
 		return MicroFencePtr(newFence());
 	}
 
-	void trimAliveFences(Bool wait);
+	void trimSignaledFences(Bool wait);
 
 private:
 	GrDynamicArray<MicroFence*> m_fences;

+ 1 - 0
AnKi/Gr/Vulkan/VkAccelerationStructure.cpp

@@ -6,6 +6,7 @@
 #include <AnKi/Gr/Vulkan/VkAccelerationStructure.h>
 #include <AnKi/Gr/Vulkan/VkGrManager.h>
 #include <AnKi/Gr/Vulkan/VkFrameGarbageCollector.h>
+#include <AnKi/Gr/Vulkan/VkBuffer.h>
 
 namespace anki {
 

+ 10 - 10
AnKi/Gr/Vulkan/VkBuffer.cpp

@@ -36,7 +36,7 @@ void* Buffer::map(PtrSize offset, PtrSize range, [[maybe_unused]] BufferMapAcces
 	}
 	ANKI_ASSERT(offset + range <= m_size);
 
-	void* ptr = getGrManagerImpl().getGpuMemoryManager().getMappedAddress(self.m_memHandle);
+	void* ptr = GpuMemoryManager::getSingleton().getMappedAddress(self.m_memHandle);
 	ANKI_ASSERT(ptr);
 
 #if ANKI_ASSERTIONS_ENABLED
@@ -187,7 +187,7 @@ Error BufferImpl::init(const BufferInitInfo& inf)
 			}
 		}
 
-		memIdx = getGrManagerImpl().getGpuMemoryManager().findMemoryType(req.memoryTypeBits, prefer, avoid);
+		memIdx = GpuMemoryManager::getSingleton().findMemoryType(req.memoryTypeBits, prefer, avoid);
 
 		// 2nd try: host & coherent
 		if(memIdx == kMaxU32)
@@ -206,7 +206,7 @@ Error BufferImpl::init(const BufferInitInfo& inf)
 				}
 			}
 
-			memIdx = getGrManagerImpl().getGpuMemoryManager().findMemoryType(req.memoryTypeBits, prefer, avoid);
+			memIdx = GpuMemoryManager::getSingleton().findMemoryType(req.memoryTypeBits, prefer, avoid);
 		}
 	}
 	else if(!!(access & BufferMapAccessBit::kRead))
@@ -214,7 +214,7 @@ Error BufferImpl::init(const BufferInitInfo& inf)
 		// Read or read/write
 
 		// Cached & coherent
-		memIdx = getGrManagerImpl().getGpuMemoryManager().findMemoryType(
+		memIdx = GpuMemoryManager::getSingleton().findMemoryType(
 			req.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, 0);
 
 		// Fallback: Just cached
@@ -225,8 +225,8 @@ Error BufferImpl::init(const BufferInitInfo& inf)
 				ANKI_VK_LOGW("Using a fallback mode for read/write buffer");
 			}
 
-			memIdx = getGrManagerImpl().getGpuMemoryManager().findMemoryType(
-				req.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, 0);
+			memIdx = GpuMemoryManager::getSingleton().findMemoryType(req.memoryTypeBits,
+																	 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, 0);
 		}
 	}
 	else
@@ -236,13 +236,13 @@ Error BufferImpl::init(const BufferInitInfo& inf)
 		ANKI_ASSERT(access == BufferMapAccessBit::kNone);
 
 		// Device only
-		memIdx = getGrManagerImpl().getGpuMemoryManager().findMemoryType(req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
-																		 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
+		memIdx = GpuMemoryManager::getSingleton().findMemoryType(req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
+																 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
 
 		// Fallback: Device with anything else
 		if(memIdx == kMaxU32)
 		{
-			memIdx = getGrManagerImpl().getGpuMemoryManager().findMemoryType(req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0);
+			memIdx = GpuMemoryManager::getSingleton().findMemoryType(req.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0);
 		}
 	}
 
@@ -267,7 +267,7 @@ Error BufferImpl::init(const BufferInitInfo& inf)
 
 	// Allocate
 	const U32 alignment = U32(max(m_mappedMemoryRangeAlignment, req.alignment));
-	getGrManagerImpl().getGpuMemoryManager().allocateMemory(memIdx, req.size, alignment, m_memHandle);
+	GpuMemoryManager::getSingleton().allocateMemory(memIdx, req.size, alignment, m_memHandle);
 
 	// Bind mem to buffer
 	{

+ 1 - 1
AnKi/Gr/Vulkan/VkCommandBuffer.cpp

@@ -1430,7 +1430,7 @@ Error CommandBufferImpl::init(const CommandBufferInitInfo& init)
 	m_tid = Thread::getCurrentThreadId();
 	m_flags = init.m_flags;
 
-	ANKI_CHECK(getGrManagerImpl().getCommandBufferFactory().newCommandBuffer(m_tid, m_flags, m_microCmdb));
+	ANKI_CHECK(CommandBufferFactory::getSingleton().newCommandBuffer(m_tid, m_flags, m_microCmdb));
 	m_handle = m_microCmdb->getHandle();
 
 	m_pool = &m_microCmdb->getFastMemoryPool();

+ 4 - 4
AnKi/Gr/Vulkan/VkCommandBufferFactory.cpp

@@ -67,7 +67,7 @@ Error CommandBufferThreadAllocator::init()
 {
 	for(GpuQueueType qtype : EnumIterable<GpuQueueType>())
 	{
-		if(m_factory->m_queueFamilies[qtype] == kMaxU32)
+		if(CommandBufferFactory::getSingleton().m_queueFamilies[qtype] == kMaxU32)
 		{
 			continue;
 		}
@@ -75,7 +75,7 @@ Error CommandBufferThreadAllocator::init()
 		VkCommandPoolCreateInfo ci = {};
 		ci.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
 		ci.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
-		ci.queueFamilyIndex = m_factory->m_queueFamilies[qtype];
+		ci.queueFamilyIndex = CommandBufferFactory::getSingleton().m_queueFamilies[qtype];
 
 		ANKI_VK_CHECK(vkCreateCommandPool(getVkDevice(), &ci, nullptr, &m_pools[qtype]));
 	}
@@ -108,7 +108,7 @@ Error CommandBufferThreadAllocator::newCommandBuffer(CommandBufferFlag cmdbFlags
 	ANKI_ASSERT(!!(cmdbFlags & CommandBufferFlag::kComputeWork) ^ !!(cmdbFlags & CommandBufferFlag::kGeneralWork));
 
 	const Bool smallBatch = !!(cmdbFlags & CommandBufferFlag::kSmallBatch);
-	const GpuQueueType queue = getQueueTypeFromCommandBufferFlags(cmdbFlags, m_factory->m_queueFamilies);
+	const GpuQueueType queue = getQueueTypeFromCommandBufferFlags(cmdbFlags, CommandBufferFactory::getSingleton().m_queueFamilies);
 
 	MicroObjectRecycler<MicroCommandBuffer>& recycler = m_recyclers[smallBatch][queue];
 
@@ -228,7 +228,7 @@ Error CommandBufferFactory::newCommandBuffer(ThreadId tid, CommandBufferFlag cmd
 
 			if(alloc == nullptr)
 			{
-				alloc = newInstance<CommandBufferThreadAllocator>(GrMemoryPool::getSingleton(), this, tid);
+				alloc = newInstance<CommandBufferThreadAllocator>(GrMemoryPool::getSingleton(), tid);
 
 				m_threadAllocs.resize(m_threadAllocs.getSize() + 1);
 				m_threadAllocs[m_threadAllocs.getSize() - 1] = alloc;

+ 12 - 15
AnKi/Gr/Vulkan/VkCommandBufferFactory.h

@@ -14,7 +14,6 @@
 namespace anki {
 
 // Forward
-class CommandBufferFactory;
 class CommandBufferThreadAllocator;
 
 /// @addtogroup vulkan
@@ -160,11 +159,9 @@ class alignas(ANKI_CACHE_LINE_SIZE) CommandBufferThreadAllocator
 	friend class MicroCommandBuffer;
 
 public:
-	CommandBufferThreadAllocator(CommandBufferFactory* factory, ThreadId tid)
-		: m_factory(factory)
-		, m_tid(tid)
+	CommandBufferThreadAllocator(ThreadId tid)
+		: m_tid(tid)
 	{
-		ANKI_ASSERT(factory);
 	}
 
 	~CommandBufferThreadAllocator()
@@ -182,7 +179,6 @@ public:
 	void deleteCommandBuffer(MicroCommandBuffer* ptr);
 
 private:
-	CommandBufferFactory* m_factory;
 	ThreadId m_tid;
 	Array<VkCommandPool, U(GpuQueueType::kCount)> m_pools = {};
 
@@ -194,26 +190,25 @@ private:
 };
 
 /// Command bufffer object recycler.
-class CommandBufferFactory
+class CommandBufferFactory : public MakeSingleton<CommandBufferFactory>
 {
 	friend class CommandBufferThreadAllocator;
 	friend class MicroCommandBuffer;
 
 public:
-	CommandBufferFactory() = default;
+	CommandBufferFactory(const VulkanQueueFamilies& queueFamilies)
+		: m_queueFamilies(queueFamilies)
+	{
+	}
 
 	CommandBufferFactory(const CommandBufferFactory&) = delete; // Non-copyable
 
-	~CommandBufferFactory() = default;
-
-	CommandBufferFactory& operator=(const CommandBufferFactory&) = delete; // Non-copyable
-
-	void init(const VulkanQueueFamilies& queueFamilies)
+	~CommandBufferFactory()
 	{
-		m_queueFamilies = queueFamilies;
+		destroy();
 	}
 
-	void destroy();
+	CommandBufferFactory& operator=(const CommandBufferFactory&) = delete; // Non-copyable
 
 	/// Request a new command buffer.
 	Error newCommandBuffer(ThreadId tid, CommandBufferFlag cmdbFlags, MicroCommandBufferPtr& ptr);
@@ -223,6 +218,8 @@ private:
 
 	GrDynamicArray<CommandBufferThreadAllocator*> m_threadAllocs;
 	RWMutex m_threadAllocMtx;
+
+	void destroy();
 };
 /// @}
 

+ 0 - 144
AnKi/Gr/Vulkan/VkDeferredBarrierFactory.h

@@ -1,144 +0,0 @@
-// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <AnKi/Gr/Vulkan/VkFenceFactory.h>
-#include <AnKi/Gr/Vulkan/VkMicroObjectRecycler.h>
-
-namespace anki {
-
-// Forward
-class DeferredBarrierFactory;
-
-/// @addtogroup vulkan
-/// @{
-
-/// Wrapper on top of VkEvent.
-class MicroDeferredBarrier
-{
-	friend class DeferredBarrierFactory;
-	friend class MicroDeferredBarrierPtrDeleter;
-
-public:
-	MicroDeferredBarrier(DeferredBarrierFactory* factory)
-		: m_factory(factory)
-	{
-		ANKI_ASSERT(factory);
-		VkEventCreateInfo ci = {};
-		ci.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
-		ANKI_VK_CHECKF(vkCreateEvent(getVkDevice(), &ci, nullptr, &m_handle));
-	}
-
-	~MicroDeferredBarrier()
-	{
-		if(m_handle)
-		{
-			vkDestroyEvent(getVkDevice(), m_handle, nullptr);
-			m_handle = VK_NULL_HANDLE;
-		}
-	}
-
-	const VkEvent& getHandle() const
-	{
-		ANKI_ASSERT(m_handle);
-		return m_handle;
-	}
-
-	void retain() const
-	{
-		m_refcount.fetchAdd(1);
-	}
-
-	I32 release() const
-	{
-		return m_refcount.fetchSub(1);
-	}
-
-	I32 getRefcount() const
-	{
-		return m_refcount.load();
-	}
-
-	void setFence(MicroFence* f)
-	{
-		m_fence.reset(f);
-	}
-
-	MicroFence* getFence() const
-	{
-		return m_fence.tryGet();
-	}
-
-	/// Interface method.
-	void onFenceDone()
-	{
-		// Do nothing
-	}
-
-private:
-	VkEvent m_handle = VK_NULL_HANDLE;
-	mutable Atomic<I32> m_refcount = {0};
-	DeferredBarrierFactory* m_factory = nullptr;
-
-	/// Fence to find out when it's safe to reuse this barrier.
-	MicroFencePtr m_fence;
-};
-
-/// Deleter for MicroDeferredBarrierPtr smart pointer.
-class MicroDeferredBarrierPtrDeleter
-{
-public:
-	void operator()(MicroDeferredBarrier* x);
-};
-
-/// MicroDeferredBarrier smart pointer.
-using MicroDeferredBarrierPtr = IntrusivePtr<MicroDeferredBarrier, MicroDeferredBarrierPtrDeleter>;
-
-/// MicroDeferredBarrier factory.
-class DeferredBarrierFactory
-{
-	friend class MicroDeferredBarrierPtrDeleter;
-	friend class MicroDeferredBarrier;
-
-public:
-	DeferredBarrierFactory() = default;
-
-	DeferredBarrierFactory(const DeferredBarrierFactory&) = delete; // Non-copyable
-
-	DeferredBarrierFactory& operator=(const DeferredBarrierFactory&) = delete; // Non-copyable
-
-	void destroy()
-	{
-		m_recycler.destroy();
-	}
-
-	MicroDeferredBarrierPtr newInstance()
-	{
-		MicroDeferredBarrier* out = m_recycler.findToReuse();
-
-		if(out == nullptr)
-		{
-			// Create a new one
-			out = anki::newInstance<MicroDeferredBarrier>(GrMemoryPool::getSingleton(), this);
-		}
-
-		return MicroDeferredBarrierPtr(out);
-	}
-
-private:
-	MicroObjectRecycler<MicroDeferredBarrier> m_recycler;
-
-	void destroyBarrier(MicroDeferredBarrier* barrier);
-};
-
-inline void MicroDeferredBarrierPtrDeleter::operator()(MicroDeferredBarrier* s)
-{
-	ANKI_ASSERT(s);
-	s->m_factory->m_recycler.recycle(s);
-}
-/// @}
-
-} // end namespace anki

+ 38 - 8
AnKi/Gr/Vulkan/VkFenceFactory.cpp

@@ -7,16 +7,18 @@
 
 namespace anki {
 
-void FenceFactory::destroy()
+void MicroFencePtrDeleter::operator()(MicroFence* f)
 {
-	LockGuard<Mutex> lock(m_mtx);
+	ANKI_ASSERT(f);
+	FenceFactory::getSingleton().deleteFence(f);
+}
 
-	for(MicroFence* fence : m_fences)
-	{
-		deleteInstance(GrMemoryPool::getSingleton(), fence);
-	}
+FenceFactory::~FenceFactory()
+{
+	trimSignaledFences(true);
 
-	m_fences.destroy();
+	ANKI_ASSERT(m_fences.getSize() == 0);
+	ANKI_ASSERT(m_aliveFenceCount == 0);
 }
 
 MicroFence* FenceFactory::newFence()
@@ -64,7 +66,7 @@ MicroFence* FenceFactory::newFence()
 	if(out == nullptr)
 	{
 		// Create a new one
-		out = anki::newInstance<MicroFence>(GrMemoryPool::getSingleton(), this);
+		out = anki::newInstance<MicroFence>(GrMemoryPool::getSingleton());
 	}
 	else
 	{
@@ -84,4 +86,32 @@ void FenceFactory::deleteFence(MicroFence* fence)
 	m_fences.emplaceBack(fence);
 }
 
+void FenceFactory::trimSignaledFences(Bool wait)
+{
+	LockGuard<Mutex> lock(m_mtx);
+
+	GrDynamicArray<MicroFence*> unsignaledFences;
+	for(MicroFence* fence : m_fences)
+	{
+		const Bool signaled = fence->clientWait((wait) ? kMaxFenceOrSemaphoreWaitTime : 0.0f);
+		if(signaled)
+		{
+			deleteInstance(GrMemoryPool::getSingleton(), fence);
+
+			ANKI_ASSERT(m_aliveFenceCount > 0);
+			--m_aliveFenceCount;
+		}
+		else
+		{
+			unsignaledFences.emplaceBack(fence);
+		}
+	}
+
+	m_fences.destroy();
+	if(unsignaledFences.getSize())
+	{
+		m_fences = std::move(unsignaledFences);
+	}
+}
+
 } // end namespace anki

+ 48 - 17
AnKi/Gr/Vulkan/VkFenceFactory.h

@@ -6,12 +6,10 @@
 #pragma once
 
 #include <AnKi/Gr/Vulkan/VkCommon.h>
+#include <AnKi/Util/Tracer.h>
 
 namespace anki {
 
-// Forward
-class FenceFactory;
-
 /// @addtogroup vulkan
 /// @{
 
@@ -22,11 +20,25 @@ class MicroFence
 	friend class MicroFencePtrDeleter;
 
 public:
-	MicroFence(FenceFactory* f);
+	MicroFence()
+	{
+		VkFenceCreateInfo ci = {};
+		ci.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+
+		ANKI_TRACE_INC_COUNTER(VkFenceCreate, 1);
+		ANKI_VK_CHECKF(vkCreateFence(getVkDevice(), &ci, nullptr, &m_handle));
+	}
 
 	MicroFence(const MicroFence&) = delete; // Non-copyable
 
-	~MicroFence();
+	~MicroFence()
+	{
+		if(m_handle)
+		{
+			ANKI_ASSERT(done());
+			vkDestroyFence(getVkDevice(), m_handle, nullptr);
+		}
+	}
 
 	MicroFence& operator=(const MicroFence&) = delete; // Non-copyable
 
@@ -55,14 +67,38 @@ public:
 		}
 	}
 
-	Bool clientWait(Second seconds);
+	Bool clientWait(Second seconds)
+	{
+		ANKI_ASSERT(m_handle);
+
+		if(seconds == 0.0)
+		{
+			return done();
+		}
+		else
+		{
+			seconds = min(seconds, kMaxFenceOrSemaphoreWaitTime);
+			const F64 nsf = 1e+9 * seconds;
+			const U64 ns = U64(nsf);
+			VkResult res;
+			ANKI_VK_CHECKF(res = vkWaitForFences(getVkDevice(), 1, &m_handle, true, ns));
+
+			return res != VK_TIMEOUT;
+		}
+	}
+
+	Bool done() const
+	{
+		ANKI_ASSERT(m_handle);
 
-	Bool done() const;
+		VkResult status;
+		ANKI_VK_CHECKF(status = vkGetFenceStatus(getVkDevice(), m_handle));
+		return status == VK_SUCCESS;
+	}
 
 private:
 	VkFence m_handle = VK_NULL_HANDLE;
 	mutable Atomic<I32> m_refcount = {0};
-	FenceFactory* m_factory = nullptr;
 };
 
 /// Deleter for FencePtr.
@@ -76,7 +112,7 @@ public:
 using MicroFencePtr = IntrusivePtr<MicroFence, MicroFencePtrDeleter>;
 
 /// A factory of fences.
-class FenceFactory
+class FenceFactory : public MakeSingleton<FenceFactory>
 {
 	friend class MicroFence;
 	friend class MicroFencePtrDeleter;
@@ -87,12 +123,7 @@ public:
 
 	FenceFactory() = default;
 
-	~FenceFactory()
-	{
-		ANKI_ASSERT(m_fences.getSize() == 0);
-	}
-
-	void destroy();
+	~FenceFactory();
 
 	/// Create a new fence pointer.
 	MicroFencePtr newInstance()
@@ -100,6 +131,8 @@ public:
 		return MicroFencePtr(newFence());
 	}
 
+	void trimSignaledFences(Bool wait);
+
 private:
 	GrDynamicArray<MicroFence*> m_fences;
 	U32 m_aliveFenceCount = 0;
@@ -111,5 +144,3 @@ private:
 /// @}
 
 } // end namespace anki
-
-#include <AnKi/Gr/Vulkan/VkFenceFactory.inl.h>

+ 0 - 66
AnKi/Gr/Vulkan/VkFenceFactory.inl.h

@@ -1,66 +0,0 @@
-// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Gr/Vulkan/VkFenceFactory.h>
-#include <AnKi/Util/Tracer.h>
-
-namespace anki {
-
-inline MicroFence::MicroFence(FenceFactory* f)
-	: m_factory(f)
-{
-	ANKI_ASSERT(f);
-	VkFenceCreateInfo ci = {};
-	ci.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
-
-	ANKI_TRACE_INC_COUNTER(VkFenceCreate, 1);
-	ANKI_VK_CHECKF(vkCreateFence(getVkDevice(), &ci, nullptr, &m_handle));
-}
-
-inline MicroFence::~MicroFence()
-{
-	if(m_handle)
-	{
-		ANKI_ASSERT(done());
-		vkDestroyFence(getVkDevice(), m_handle, nullptr);
-	}
-}
-
-inline Bool MicroFence::done() const
-{
-	ANKI_ASSERT(m_handle);
-	VkResult status;
-	ANKI_VK_CHECKF(status = vkGetFenceStatus(getVkDevice(), m_handle));
-	return status == VK_SUCCESS;
-}
-
-inline Bool MicroFence::clientWait(Second seconds)
-{
-	ANKI_ASSERT(m_handle);
-
-	if(seconds == 0.0)
-	{
-		return done();
-	}
-	else
-	{
-		seconds = min(seconds, kMaxFenceOrSemaphoreWaitTime);
-
-		const F64 nsf = 1e+9 * seconds;
-		const U64 ns = U64(nsf);
-		VkResult res;
-		ANKI_VK_CHECKF(res = vkWaitForFences(getVkDevice(), 1, &m_handle, true, ns));
-
-		return res != VK_TIMEOUT;
-	}
-}
-
-inline void MicroFencePtrDeleter::operator()(MicroFence* f)
-{
-	ANKI_ASSERT(f);
-	f->m_factory->deleteFence(f);
-}
-
-} // end namespace anki

+ 2 - 2
AnKi/Gr/Vulkan/VkFrameGarbageCollector.cpp

@@ -60,7 +60,7 @@ void FrameGarbageCollector::collectGarbage()
 
 			if(textureGarbage->m_memoryHandle)
 			{
-				getGrManagerImpl().getGpuMemoryManager().freeMemory(textureGarbage->m_memoryHandle);
+				GpuMemoryManager::getSingleton().freeMemory(textureGarbage->m_memoryHandle);
 			}
 
 			deleteInstance(GrMemoryPool::getSingleton(), textureGarbage);
@@ -83,7 +83,7 @@ void FrameGarbageCollector::collectGarbage()
 
 			if(bufferGarbage->m_memoryHandle)
 			{
-				getGrManagerImpl().getGpuMemoryManager().freeMemory(bufferGarbage->m_memoryHandle);
+				GpuMemoryManager::getSingleton().freeMemory(bufferGarbage->m_memoryHandle);
 			}
 
 			deleteInstance(GrMemoryPool::getSingleton(), bufferGarbage);

+ 0 - 8
AnKi/Gr/Vulkan/VkGpuMemoryManager.cpp

@@ -70,14 +70,6 @@ void GpuMemoryManagerInterface::freeChunk(GpuMemoryManagerChunk* chunk)
 	deleteInstance(GrMemoryPool::getSingleton(), chunk);
 }
 
-GpuMemoryManager::GpuMemoryManager()
-{
-}
-
-GpuMemoryManager::~GpuMemoryManager()
-{
-}
-
 void GpuMemoryManager::destroy()
 {
 	ANKI_VK_LOGV("Destroying memory manager");

+ 13 - 7
AnKi/Gr/Vulkan/VkGpuMemoryManager.h

@@ -105,23 +105,25 @@ private:
 };
 
 /// Dynamic GPU memory allocator for all types.
-class GpuMemoryManager
+class GpuMemoryManager : public MakeSingleton<GpuMemoryManager>
 {
 	friend class GpuMemoryManagerInterface;
 
 public:
-	GpuMemoryManager();
+	GpuMemoryManager(Bool exposeBufferGpuAddress)
+	{
+		init(exposeBufferGpuAddress);
+	}
 
 	GpuMemoryManager(const GpuMemoryManager&) = delete; // Non-copyable
 
-	~GpuMemoryManager();
+	~GpuMemoryManager()
+	{
+		destroy();
+	}
 
 	GpuMemoryManager& operator=(const GpuMemoryManager&) = delete; // Non-copyable
 
-	void init(Bool exposeBufferGpuAddress);
-
-	void destroy();
-
 	/// Allocate memory.
 	void allocateMemory(U32 memTypeIdx, PtrSize size, U32 alignment, GpuMemoryHandle& handle);
 
@@ -149,6 +151,10 @@ private:
 	// Dedicated allocation stats
 	Atomic<PtrSize> m_dedicatedAllocatedMemory = {0};
 	Atomic<U32> m_dedicatedAllocationCount = {0};
+
+	void init(Bool exposeBufferGpuAddress);
+
+	void destroy();
 };
 /// @}
 

+ 31 - 48
AnKi/Gr/Vulkan/VkGrManager.cpp

@@ -21,6 +21,7 @@
 #include <AnKi/Gr/Vulkan/VkAccelerationStructure.h>
 #include <AnKi/Gr/Vulkan/VkGrUpscaler.h>
 #include <AnKi/Gr/Vulkan/VkFence.h>
+#include <AnKi/Gr/Vulkan/VkGpuMemoryManager.h>
 
 #include <AnKi/Window/NativeWindow.h>
 #if ANKI_WINDOWING_SYSTEM_SDL
@@ -216,7 +217,7 @@ GrManagerImpl::~GrManagerImpl()
 	}
 
 	// 3rd THING: The destroy everything that has a reference to GrObjects.
-	m_cmdbFactory.destroy();
+	CommandBufferFactory::freeSingleton();
 
 	for(PerFrame& frame : m_perFrame)
 	{
@@ -229,23 +230,20 @@ GrManagerImpl::~GrManagerImpl()
 
 	// 4th THING: Continue with the rest
 
-	m_barrierFactory.destroy(); // Destroy before fences
-	m_semaphoreFactory.destroy(); // Destroy before fences
-	m_swapchainFactory.destroy(); // Destroy before fences
+	OcclusionQueryFactory::freeSingleton();
+	TimestampQueryFactory::freeSingleton();
+	PrimitivesPassedClippingFactory::freeSingleton();
+	SemaphoreFactory::freeSingleton(); // Destroy before fences
+	SamplerFactory::freeSingleton();
+	SwapchainFactory::freeSingleton(); // Destroy before fences
 	m_frameGarbageCollector.destroy();
 
-	m_gpuMemManager.destroy();
-
+	GpuMemoryManager::freeSingleton();
 	PipelineLayoutFactory::freeSingleton();
-
 	DSLayoutFactory::freeSingleton();
 	DSBindless::freeSingleton();
-
-	m_pplineCache.destroy();
-
-	m_fenceFactory.destroy();
-
-	m_samplerFactory.destroy();
+	PipelineCache::freeSingleton();
+	FenceFactory::freeSingleton();
 
 	if(m_device)
 	{
@@ -273,10 +271,6 @@ GrManagerImpl::~GrManagerImpl()
 		vkDestroyInstance(m_instance, pallocCbs);
 	}
 
-#if ANKI_PLATFORM_MOBILE
-	anki::deleteInstance(GrMemoryPool::getSingleton(), m_globalCreatePipelineMtx);
-#endif
-
 	m_cacheDir.destroy();
 
 	GrMemoryPool::freeSingleton();
@@ -318,29 +312,27 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 		}
 	}
 
-	m_swapchainFactory.init(g_vsyncCVar.get());
-
-	m_crntSwapchain = m_swapchainFactory.newInstance();
+	SwapchainFactory::allocateSingleton(g_vsyncCVar.get());
+	m_crntSwapchain = SwapchainFactory::getSingleton().newInstance();
 
-	ANKI_CHECK(m_pplineCache.init(init.m_cacheDirectory));
+	PipelineCache::allocateSingleton();
+	ANKI_CHECK(PipelineCache::getSingleton().init(init.m_cacheDirectory));
 
 	ANKI_CHECK(initMemory());
 
-	m_cmdbFactory.init(m_queueFamilyIndices);
+	CommandBufferFactory::allocateSingleton(m_queueFamilyIndices);
+	FenceFactory::allocateSingleton();
+	SemaphoreFactory::allocateSingleton();
+	OcclusionQueryFactory::allocateSingleton();
+	TimestampQueryFactory::allocateSingleton();
+	PrimitivesPassedClippingFactory::allocateSingleton();
+	SamplerFactory::allocateSingleton();
 
 	for(PerFrame& f : m_perFrame)
 	{
 		resetFrame(f);
 	}
 
-	m_occlusionQueryFactory.init(VK_QUERY_TYPE_OCCLUSION);
-	m_timestampQueryFactory.init(VK_QUERY_TYPE_TIMESTAMP);
-	if(m_capabilities.m_pipelineQuery)
-	{
-		m_pipelineQueryFactories[PipelineQueryType::kPrimitivesPassedClipping].init(VK_QUERY_TYPE_PIPELINE_STATISTICS,
-																					VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT);
-	}
-
 	// See if unaligned formats are supported
 	{
 		m_capabilities.m_unalignedBbpTextureFormats = true;
@@ -738,15 +730,6 @@ Error GrManagerImpl::initInstance()
 	m_capabilities.m_shaderGroupHandleSize = m_rtPipelineProps.shaderGroupHandleSize;
 	m_capabilities.m_sbtRecordAlignment = m_rtPipelineProps.shaderGroupBaseAlignment;
 
-#if ANKI_PLATFORM_MOBILE
-	if(m_capabilities.m_gpuVendor == GpuVendor::kQualcomm)
-	{
-		// Calling vkCreateGraphicsPipeline from multiple threads crashes qualcomm's compiler
-		ANKI_VK_LOGI("Enabling workaround for vkCreateGraphicsPipeline crashing when called from multiple threads");
-		m_globalCreatePipelineMtx = anki::newInstance<Mutex>(GrMemoryPool::getSingleton());
-	}
-#endif
-
 	// DLSS checks
 	m_capabilities.m_dlss = ANKI_DLSS && m_capabilities.m_gpuVendor == GpuVendor::kNvidia;
 
@@ -1421,7 +1404,7 @@ Error GrManagerImpl::initMemory()
 					 ANKI_FORMAT_U32(m_memoryProperties.memoryTypes[i].propertyFlags));
 	}
 
-	m_gpuMemManager.init(!!(m_extensions & VulkanExtensions::kKHR_buffer_device_address));
+	GpuMemoryManager::allocateSingleton(!!(m_extensions & VulkanExtensions::kKHR_buffer_device_address));
 
 	return Error::kNone;
 }
@@ -1496,8 +1479,8 @@ TexturePtr GrManagerImpl::acquireNextPresentableTexture()
 	PerFrame& frame = m_perFrame[m_frame % kMaxFramesInFlight];
 
 	// Create sync objects
-	MicroFencePtr fence = m_fenceFactory.newInstance();
-	frame.m_acquireSemaphore = m_semaphoreFactory.newInstance(fence, false);
+	MicroFencePtr fence = FenceFactory::getSingleton().newInstance();
+	frame.m_acquireSemaphore = SemaphoreFactory::getSingleton().newInstance(fence, false);
 
 	// Get new image
 	uint32_t imageIdx;
@@ -1516,7 +1499,7 @@ TexturePtr GrManagerImpl::acquireNextPresentableTexture()
 			}
 		}
 		m_crntSwapchain.reset(nullptr);
-		m_crntSwapchain = m_swapchainFactory.newInstance();
+		m_crntSwapchain = SwapchainFactory::getSingleton().newInstance();
 
 		// Can't fail a second time
 		ANKI_VK_CHECKF(vkAcquireNextImageKHR(m_device, m_crntSwapchain->m_swapchain, UINT64_MAX, frame.m_acquireSemaphore->getHandle(),
@@ -1579,7 +1562,7 @@ void GrManagerImpl::endFrame()
 		}
 		vkDeviceWaitIdle(m_device);
 		m_crntSwapchain.reset(nullptr);
-		m_crntSwapchain = m_swapchainFactory.newInstance();
+		m_crntSwapchain = SwapchainFactory::getSingleton().newInstance();
 	}
 	else
 	{
@@ -1587,7 +1570,7 @@ void GrManagerImpl::endFrame()
 		ANKI_VK_CHECKF(res);
 	}
 
-	m_gpuMemManager.updateStats();
+	GpuMemoryManager::getSingleton().updateStats();
 
 	// Finalize
 	++m_frame;
@@ -1615,7 +1598,7 @@ void GrManagerImpl::flushCommandBuffers(WeakArray<MicroCommandBuffer*> cmdbs, Bo
 	submit.pWaitDstStageMask = &waitStages[0];
 
 	// First thing, create a fence
-	MicroFencePtr fence = m_fenceFactory.newInstance();
+	MicroFencePtr fence = FenceFactory::getSingleton().newInstance();
 
 	// Command buffers
 	Array<VkCommandBuffer, 16> handles;
@@ -1659,7 +1642,7 @@ void GrManagerImpl::flushCommandBuffers(WeakArray<MicroCommandBuffer*> cmdbs, Bo
 
 	if(userSignalSemaphore)
 	{
-		*userSignalSemaphore = m_semaphoreFactory.newInstance(fence, true);
+		*userSignalSemaphore = SemaphoreFactory::getSingleton().newInstance(fence, true);
 
 		signalSemaphores[submit.signalSemaphoreCount++] = (*userSignalSemaphore)->getHandle();
 		signalTimelineValues[timelineInfo.signalSemaphoreValueCount++] = (*userSignalSemaphore)->getNextSemaphoreValue();
@@ -1686,7 +1669,7 @@ void GrManagerImpl::flushCommandBuffers(WeakArray<MicroCommandBuffer*> cmdbs, Bo
 
 			// Create the semaphore to signal
 			ANKI_ASSERT(!frame.m_renderSemaphore && "Only one begin/end render pass is allowed with the default fb");
-			frame.m_renderSemaphore = m_semaphoreFactory.newInstance(fence, false);
+			frame.m_renderSemaphore = SemaphoreFactory::getSingleton().newInstance(fence, false);
 
 			signalSemaphores[submit.signalSemaphoreCount++] = frame.m_renderSemaphore->getHandle();
 

+ 1 - 95
AnKi/Gr/Vulkan/VkGrManager.h

@@ -7,17 +7,9 @@
 
 #include <AnKi/Gr/GrManager.h>
 #include <AnKi/Gr/Vulkan/VkCommon.h>
-#include <AnKi/Gr/Vulkan/VkGpuMemoryManager.h>
 #include <AnKi/Gr/Vulkan/VkSemaphoreFactory.h>
-#include <AnKi/Gr/Vulkan/VkDeferredBarrierFactory.h>
 #include <AnKi/Gr/Vulkan/VkFenceFactory.h>
-#include <AnKi/Gr/Vulkan/VkSamplerFactory.h>
-#include <AnKi/Gr/Vulkan/VkQueryFactory.h>
-#include <AnKi/Gr/Vulkan/VkDescriptorSet.h>
-#include <AnKi/Gr/Vulkan/VkCommandBufferFactory.h>
 #include <AnKi/Gr/Vulkan/VkSwapchainFactory.h>
-#include <AnKi/Gr/Vulkan/VkPipelineLayout.h>
-#include <AnKi/Gr/Vulkan/VkPipelineCache.h>
 #include <AnKi/Gr/Vulkan/VkFrameGarbageCollector.h>
 #include <AnKi/Util/File.h>
 
@@ -28,6 +20,7 @@ namespace anki {
 
 // Forward
 class TextureFallbackUploader;
+class MicroCommandBuffer;
 
 /// @addtogroup vulkan
 /// @{
@@ -84,67 +77,13 @@ public:
 		return m_instance;
 	}
 
-	/// @name object_creation
-	/// @{
-
-	CommandBufferFactory& getCommandBufferFactory()
-	{
-		return m_cmdbFactory;
-	}
-
-	const CommandBufferFactory& getCommandBufferFactory() const
-	{
-		return m_cmdbFactory;
-	}
-
-	SamplerFactory& getSamplerFactory()
-	{
-		return m_samplerFactory;
-	}
-	/// @}
-
 	void flushCommandBuffers(WeakArray<MicroCommandBuffer*> cmdbs, Bool cmdbRenderedToSwapchain, WeakArray<MicroSemaphore*> waitSemaphores,
 							 MicroSemaphorePtr* signalSemaphore, Bool wait);
 
-	/// @name Memory
-	/// @{
-	GpuMemoryManager& getGpuMemoryManager()
-	{
-		return m_gpuMemManager;
-	}
-
-	const GpuMemoryManager& getGpuMemoryManager() const
-	{
-		return m_gpuMemManager;
-	}
-
 	const VkPhysicalDeviceMemoryProperties& getMemoryProperties() const
 	{
 		return m_memoryProperties;
 	}
-	/// @}
-
-	QueryFactory& getOcclusionQueryFactory()
-	{
-		return m_occlusionQueryFactory;
-	}
-
-	QueryFactory& getTimestampQueryFactory()
-	{
-		return m_timestampQueryFactory;
-	}
-
-	QueryFactory& getPipelineQueryFactory(PipelineQueryType type)
-	{
-		ANKI_ASSERT(m_capabilities.m_pipelineQuery);
-		return m_pipelineQueryFactories[type];
-	}
-
-	VkPipelineCache getPipelineCache() const
-	{
-		ANKI_ASSERT(m_pplineCache.m_cacheHandle);
-		return m_pplineCache.m_cacheHandle;
-	}
 
 	VulkanExtensions getExtensions() const
 	{
@@ -189,13 +128,6 @@ public:
 		return m_frameGarbageCollector;
 	}
 
-#if ANKI_PLATFORM_MOBILE
-	Mutex* getGlobalCreatePipelineMutex() const
-	{
-		return m_globalCreatePipelineMtx;
-	}
-#endif
-
 private:
 	U64 m_frame = 0;
 
@@ -253,36 +185,10 @@ private:
 	Array<PerFrame, kMaxFramesInFlight> m_perFrame;
 	/// @}
 
-	/// @name Memory
-	/// @{
 	VkPhysicalDeviceMemoryProperties m_memoryProperties;
 
-	/// The main allocator.
-	GpuMemoryManager m_gpuMemManager;
-	/// @}
-
-	CommandBufferFactory m_cmdbFactory;
-
-	FenceFactory m_fenceFactory;
-	SemaphoreFactory m_semaphoreFactory;
-	DeferredBarrierFactory m_barrierFactory;
-	SamplerFactory m_samplerFactory;
-	/// @}
-
-	SwapchainFactory m_swapchainFactory;
-
-	QueryFactory m_occlusionQueryFactory;
-	QueryFactory m_timestampQueryFactory;
-	Array<QueryFactory, U32(PipelineQueryType::kCount)> m_pipelineQueryFactories;
-
-	PipelineCache m_pplineCache;
-
 	FrameGarbageCollector m_frameGarbageCollector;
 
-#if ANKI_PLATFORM_MOBILE
-	Mutex* m_globalCreatePipelineMtx = nullptr;
-#endif
-
 	Error initInternal(const GrManagerInitInfo& init);
 	Error initInstance();
 	Error initSurface();

+ 2 - 2
AnKi/Gr/Vulkan/VkOcclusionQuery.cpp

@@ -29,13 +29,13 @@ OcclusionQueryImpl::~OcclusionQueryImpl()
 {
 	if(m_handle)
 	{
-		getGrManagerImpl().getOcclusionQueryFactory().deleteQuery(m_handle);
+		OcclusionQueryFactory::getSingleton().deleteQuery(m_handle);
 	}
 }
 
 Error OcclusionQueryImpl::init()
 {
-	ANKI_CHECK(getGrManagerImpl().getOcclusionQueryFactory().newQuery(m_handle));
+	ANKI_CHECK(OcclusionQueryFactory::getSingleton().newQuery(m_handle));
 	return Error::kNone;
 }
 

+ 129 - 5
AnKi/Gr/Vulkan/VkPipeline.cpp

@@ -7,9 +7,13 @@
 #include <AnKi/Gr/Vulkan/VkGrManager.h>
 #include <AnKi/Gr/Common/Functions.h>
 #include <AnKi/Util/Tracer.h>
+#include <AnKi/Util/Filesystem.h>
 
 namespace anki {
 
+static NumericCVar<PtrSize> g_diskShaderCacheMaxSizeCVar(CVarSubsystem::kGr, "DiskShaderCacheMaxSize", 128_MB, 1_MB, 1_GB,
+														 "Max size of the pipeline cache file");
+
 void PipelineStateTracker::reset()
 {
 	m_state.reset();
@@ -482,18 +486,18 @@ void PipelineFactory::getOrCreatePipeline(PipelineStateTracker& state, Pipeline&
 		ANKI_TRACE_SCOPED_EVENT(VkPipelineCreate);
 
 #if ANKI_PLATFORM_MOBILE
-		if(m_globalCreatePipelineMtx)
+		if(PipelineCache::getSingleton().m_globalCreatePipelineMtx)
 		{
-			m_globalCreatePipelineMtx->lock();
+			PipelineCache::getSingleton().m_globalCreatePipelineMtx->lock();
 		}
 #endif
 
-		ANKI_VK_CHECKF(vkCreateGraphicsPipelines(getVkDevice(), m_pplineCache, 1, &ci, nullptr, &pp.m_handle));
+		ANKI_VK_CHECKF(vkCreateGraphicsPipelines(getVkDevice(), PipelineCache::getSingleton().m_cacheHandle, 1, &ci, nullptr, &pp.m_handle));
 
 #if ANKI_PLATFORM_MOBILE
-		if(m_globalCreatePipelineMtx)
+		if(PipelineCache::getSingleton().m_globalCreatePipelineMtx)
 		{
-			m_globalCreatePipelineMtx->unlock();
+			PipelineCache::getSingleton().m_globalCreatePipelineMtx->unlock();
 		}
 #endif
 	}
@@ -507,4 +511,124 @@ void PipelineFactory::getOrCreatePipeline(PipelineStateTracker& state, Pipeline&
 	getGrManagerImpl().printPipelineShaderInfo(pp.m_handle, state.m_state.m_prog->getName(), hash);
 }
 
+Error PipelineCache::init(CString cacheDir)
+{
+	ANKI_ASSERT(cacheDir);
+	m_dumpSize = g_diskShaderCacheMaxSizeCVar.get();
+	m_dumpFilename.sprintf("%s/VkPipelineCache", cacheDir.cstr());
+
+	// Try read the pipeline cache file.
+	GrDynamicArray<U8, PtrSize> diskDump;
+	if(fileExists(m_dumpFilename.toCString()))
+	{
+		File file;
+		ANKI_CHECK(file.open(m_dumpFilename.toCString(), FileOpenFlag::kBinary | FileOpenFlag::kRead));
+
+		const PtrSize diskDumpSize = file.getSize();
+		if(diskDumpSize <= sizeof(U8) * VK_UUID_SIZE)
+		{
+			ANKI_VK_LOGI("Pipeline cache dump appears to be empty: %s", &m_dumpFilename[0]);
+		}
+		else
+		{
+			// Get current pipeline UUID and compare it with the cache's
+			VkPhysicalDeviceProperties props;
+			vkGetPhysicalDeviceProperties(getGrManagerImpl().getPhysicalDevice(), &props);
+
+			Array<U8, VK_UUID_SIZE> cacheUuid;
+			ANKI_CHECK(file.read(&cacheUuid[0], VK_UUID_SIZE));
+
+			if(memcmp(&cacheUuid[0], &props.pipelineCacheUUID[0], VK_UUID_SIZE) != 0)
+			{
+				ANKI_VK_LOGI("Pipeline cache dump is not compatible with the current device: %s", &m_dumpFilename[0]);
+			}
+			else
+			{
+				diskDump.resize(diskDumpSize - VK_UUID_SIZE);
+				ANKI_CHECK(file.read(&diskDump[0], diskDumpSize - VK_UUID_SIZE));
+			}
+		}
+	}
+	else
+	{
+		ANKI_VK_LOGI("Pipeline cache dump not found: %s", &m_dumpFilename[0]);
+	}
+
+	// Create the cache
+	VkPipelineCacheCreateInfo ci = {};
+	ci.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
+	if(diskDump.getSize())
+	{
+		ANKI_VK_LOGI("Will load %zu bytes of pipeline cache", diskDump.getSize());
+		ci.initialDataSize = diskDump.getSize();
+		ci.pInitialData = &diskDump[0];
+	}
+
+	ANKI_VK_CHECK(vkCreatePipelineCache(getVkDevice(), &ci, nullptr, &m_cacheHandle));
+
+#if ANKI_PLATFORM_MOBILE
+	ANKI_ASSERT(GrManager::getSingleton().getDeviceCapabilities() != GpuVendor::kNone);
+	if(GrManager::getSingleton().getDeviceCapabilities().m_gpuVendor == GpuVendor::kQualcomm)
+	{
+		// Calling vkCreateGraphicsPipeline from multiple threads crashes qualcomm's compiler
+		ANKI_VK_LOGI("Enabling workaround for vkCreateGraphicsPipeline crashing when called from multiple threads");
+		m_globalCreatePipelineMtx = anki::newInstance<Mutex>(GrMemoryPool::getSingleton());
+	}
+#endif
+
+	return Error::kNone;
+}
+
+void PipelineCache::destroy()
+{
+	const Error err = destroyInternal();
+	if(err)
+	{
+		ANKI_VK_LOGE("An error occurred while storing the pipeline cache to disk. Will ignore");
+	}
+
+	m_dumpFilename.destroy();
+}
+
+Error PipelineCache::destroyInternal()
+{
+#if ANKI_PLATFORM_MOBILE
+	deleteInstance(GrMemoryPool::getSingleton(), m_globalCreatePipelineMtx);
+#endif
+
+	if(m_cacheHandle)
+	{
+		// Get size of cache
+		size_t size = 0;
+		ANKI_VK_CHECK(vkGetPipelineCacheData(getVkDevice(), m_cacheHandle, &size, nullptr));
+		size = min(size, m_dumpSize);
+
+		if(size > 0)
+		{
+			// Read cache
+			GrDynamicArray<U8, PtrSize> cacheData;
+			cacheData.resize(size);
+			ANKI_VK_CHECK(vkGetPipelineCacheData(getVkDevice(), m_cacheHandle, &size, &cacheData[0]));
+
+			// Write file
+			File file;
+			ANKI_CHECK(file.open(&m_dumpFilename[0], FileOpenFlag::kBinary | FileOpenFlag::kWrite));
+
+			VkPhysicalDeviceProperties props;
+			vkGetPhysicalDeviceProperties(getGrManagerImpl().getPhysicalDevice(), &props);
+
+			ANKI_CHECK(file.write(&props.pipelineCacheUUID[0], VK_UUID_SIZE));
+			ANKI_CHECK(file.write(&cacheData[0], size));
+
+			ANKI_VK_LOGI("Dumped %zu bytes of the pipeline cache", size);
+		}
+
+		// Destroy cache
+		vkDestroyPipelineCache(getVkDevice(), m_cacheHandle, nullptr);
+		m_cacheHandle = VK_NULL_HANDLE;
+	}
+
+	return Error::kNone;
+}
+
 } // end namespace anki

+ 22 - 15
AnKi/Gr/Vulkan/VkPipeline.h

@@ -561,19 +561,6 @@ public:
 
 	~PipelineFactory();
 
-	void init(VkPipelineCache pplineCache
-#if ANKI_PLATFORM_MOBILE
-			  ,
-			  Mutex* globalCreatePipelineMtx
-#endif
-	)
-	{
-		m_pplineCache = pplineCache;
-#if ANKI_PLATFORM_MOBILE
-		m_globalCreatePipelineMtx = globalCreatePipelineMtx;
-#endif
-	}
-
 	void destroy();
 
 	/// @note Thread-safe.
@@ -583,13 +570,33 @@ private:
 	class PipelineInternal;
 	class Hasher;
 
-	VkPipelineCache m_pplineCache = VK_NULL_HANDLE;
-
 	GrHashMap<U64, PipelineInternal, Hasher> m_pplines;
 	RWMutex m_pplinesMtx;
+};
+
+/// On disk pipeline cache.
+class PipelineCache : public MakeSingleton<PipelineCache>
+{
+public:
+	VkPipelineCache m_cacheHandle = VK_NULL_HANDLE;
 #if ANKI_PLATFORM_MOBILE
+	/// Workaround a bug in Qualcomm
 	Mutex* m_globalCreatePipelineMtx = nullptr;
 #endif
+
+	~PipelineCache()
+	{
+		destroy();
+	}
+
+	Error init(CString cacheDir);
+
+private:
+	GrString m_dumpFilename;
+	PtrSize m_dumpSize = 0;
+
+	void destroy();
+	Error destroyInternal();
 };
 /// @}
 

+ 0 - 123
AnKi/Gr/Vulkan/VkPipelineCache.cpp

@@ -1,123 +0,0 @@
-// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Gr/Vulkan/VkPipelineCache.h>
-#include <AnKi/Gr/Vulkan/VkGrManager.h>
-#include <AnKi/Core/CVarSet.h>
-#include <AnKi/Util/Filesystem.h>
-#include <AnKi/Util/File.h>
-
-namespace anki {
-
-static NumericCVar<PtrSize> g_diskShaderCacheMaxSizeCVar(CVarSubsystem::kGr, "DiskShaderCacheMaxSize", 128_MB, 1_MB, 1_GB,
-														 "Max size of the pipeline cache file");
-
-Error PipelineCache::init(CString cacheDir)
-{
-	ANKI_ASSERT(cacheDir);
-	m_dumpSize = g_diskShaderCacheMaxSizeCVar.get();
-	m_dumpFilename.sprintf("%s/VkPipelineCache", cacheDir.cstr());
-
-	// Try read the pipeline cache file.
-	GrDynamicArray<U8, PtrSize> diskDump;
-	if(fileExists(m_dumpFilename.toCString()))
-	{
-		File file;
-		ANKI_CHECK(file.open(m_dumpFilename.toCString(), FileOpenFlag::kBinary | FileOpenFlag::kRead));
-
-		const PtrSize diskDumpSize = file.getSize();
-		if(diskDumpSize <= sizeof(U8) * VK_UUID_SIZE)
-		{
-			ANKI_VK_LOGI("Pipeline cache dump appears to be empty: %s", &m_dumpFilename[0]);
-		}
-		else
-		{
-			// Get current pipeline UUID and compare it with the cache's
-			VkPhysicalDeviceProperties props;
-			vkGetPhysicalDeviceProperties(getGrManagerImpl().getPhysicalDevice(), &props);
-
-			Array<U8, VK_UUID_SIZE> cacheUuid;
-			ANKI_CHECK(file.read(&cacheUuid[0], VK_UUID_SIZE));
-
-			if(memcmp(&cacheUuid[0], &props.pipelineCacheUUID[0], VK_UUID_SIZE) != 0)
-			{
-				ANKI_VK_LOGI("Pipeline cache dump is not compatible with the current device: %s", &m_dumpFilename[0]);
-			}
-			else
-			{
-				diskDump.resize(diskDumpSize - VK_UUID_SIZE);
-				ANKI_CHECK(file.read(&diskDump[0], diskDumpSize - VK_UUID_SIZE));
-			}
-		}
-	}
-	else
-	{
-		ANKI_VK_LOGI("Pipeline cache dump not found: %s", &m_dumpFilename[0]);
-	}
-
-	// Create the cache
-	VkPipelineCacheCreateInfo ci = {};
-	ci.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
-	if(diskDump.getSize())
-	{
-		ANKI_VK_LOGI("Will load %zu bytes of pipeline cache", diskDump.getSize());
-		ci.initialDataSize = diskDump.getSize();
-		ci.pInitialData = &diskDump[0];
-	}
-
-	ANKI_VK_CHECK(vkCreatePipelineCache(getVkDevice(), &ci, nullptr, &m_cacheHandle));
-
-	return Error::kNone;
-}
-
-void PipelineCache::destroy()
-{
-	const Error err = destroyInternal();
-	if(err)
-	{
-		ANKI_VK_LOGE("An error occurred while storing the pipeline cache to disk. Will ignore");
-	}
-
-	m_dumpFilename.destroy();
-}
-
-Error PipelineCache::destroyInternal()
-{
-	if(m_cacheHandle)
-	{
-		// Get size of cache
-		size_t size = 0;
-		ANKI_VK_CHECK(vkGetPipelineCacheData(getVkDevice(), m_cacheHandle, &size, nullptr));
-		size = min(size, m_dumpSize);
-
-		if(size > 0)
-		{
-			// Read cache
-			GrDynamicArray<U8, PtrSize> cacheData;
-			cacheData.resize(size);
-			ANKI_VK_CHECK(vkGetPipelineCacheData(getVkDevice(), m_cacheHandle, &size, &cacheData[0]));
-
-			// Write file
-			File file;
-			ANKI_CHECK(file.open(&m_dumpFilename[0], FileOpenFlag::kBinary | FileOpenFlag::kWrite));
-
-			VkPhysicalDeviceProperties props;
-			vkGetPhysicalDeviceProperties(getGrManagerImpl().getPhysicalDevice(), &props);
-
-			ANKI_CHECK(file.write(&props.pipelineCacheUUID[0], VK_UUID_SIZE));
-			ANKI_CHECK(file.write(&cacheData[0], size));
-
-			ANKI_VK_LOGI("Dumped %zu bytes of the pipeline cache", size);
-		}
-
-		// Destroy cache
-		vkDestroyPipelineCache(getVkDevice(), m_cacheHandle, nullptr);
-		m_cacheHandle = VK_NULL_HANDLE;
-	}
-
-	return Error::kNone;
-}
-
-} // end namespace anki

+ 0 - 33
AnKi/Gr/Vulkan/VkPipelineCache.h

@@ -1,33 +0,0 @@
-// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <AnKi/Gr/Vulkan/VkCommon.h>
-
-namespace anki {
-
-/// @addtogroup vulkan
-/// @{
-
-/// On disk pipeline cache.
-class PipelineCache
-{
-public:
-	VkPipelineCache m_cacheHandle = VK_NULL_HANDLE;
-
-	Error init(CString cacheDir);
-
-	void destroy();
-
-private:
-	GrString m_dumpFilename;
-	PtrSize m_dumpSize = 0;
-
-	Error destroyInternal();
-};
-/// @}
-
-} // end namespace anki

+ 2 - 2
AnKi/Gr/Vulkan/VkPipelineQuery.cpp

@@ -53,7 +53,7 @@ PipelineQueryImpl::~PipelineQueryImpl()
 {
 	if(m_handle)
 	{
-		getGrManagerImpl().getPipelineQueryFactory(m_type).deleteQuery(m_handle);
+		PrimitivesPassedClippingFactory::getSingleton().deleteQuery(m_handle);
 	}
 }
 
@@ -61,7 +61,7 @@ Error PipelineQueryImpl::init(PipelineQueryType type)
 {
 	ANKI_ASSERT(type < PipelineQueryType::kCount);
 	m_type = type;
-	ANKI_CHECK(getGrManagerImpl().getPipelineQueryFactory(type).newQuery(m_handle));
+	ANKI_CHECK(PrimitivesPassedClippingFactory::getSingleton().newQuery(m_handle));
 	return Error::kNone;
 }
 

+ 30 - 7
AnKi/Gr/Vulkan/VkQueryFactory.h

@@ -64,8 +64,10 @@ private:
 class QueryFactory
 {
 public:
-	QueryFactory()
+	QueryFactory(VkQueryType poolType, VkQueryPipelineStatisticFlags pplineStatisticsFlags = 0)
 	{
+		m_poolType = poolType;
+		m_pplineStatisticsFlags = pplineStatisticsFlags;
 	}
 
 	QueryFactory(const QueryFactory&) = delete; // Non-copyable
@@ -74,12 +76,6 @@ public:
 
 	QueryFactory& operator=(const QueryFactory&) = delete; // Non-copyable
 
-	void init(VkQueryType poolType, VkQueryPipelineStatisticFlags pplineStatisticsFlags = 0)
-	{
-		m_poolType = poolType;
-		m_pplineStatisticsFlags = pplineStatisticsFlags;
-	}
-
 	/// @note It's thread-safe.
 	Error newQuery(MicroQuery& handle);
 
@@ -94,6 +90,33 @@ private:
 	VkQueryType m_poolType = VK_QUERY_TYPE_MAX_ENUM;
 	VkQueryPipelineStatisticFlags m_pplineStatisticsFlags = 0;
 };
+
+class OcclusionQueryFactory : public QueryFactory, public MakeSingleton<OcclusionQueryFactory>
+{
+public:
+	OcclusionQueryFactory()
+		: QueryFactory(VK_QUERY_TYPE_OCCLUSION)
+	{
+	}
+};
+
+class TimestampQueryFactory : public QueryFactory, public MakeSingleton<TimestampQueryFactory>
+{
+public:
+	TimestampQueryFactory()
+		: QueryFactory(VK_QUERY_TYPE_TIMESTAMP)
+	{
+	}
+};
+
+class PrimitivesPassedClippingFactory : public QueryFactory, public MakeSingleton<PrimitivesPassedClippingFactory>
+{
+public:
+	PrimitivesPassedClippingFactory()
+		: QueryFactory(VK_QUERY_TYPE_PIPELINE_STATISTICS, VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT)
+	{
+	}
+};
 /// @}
 
 } // end namespace anki

+ 157 - 1
AnKi/Gr/Vulkan/VkSampler.cpp

@@ -8,10 +8,166 @@
 
 namespace anki {
 
+MicroSampler::~MicroSampler()
+{
+	if(m_handle)
+	{
+		vkDestroySampler(getVkDevice(), m_handle, nullptr);
+	}
+}
+
+Error MicroSampler::init(const SamplerInitInfo& inf)
+{
+	// Fill the create cio
+	VkSamplerCreateInfo ci = {};
+	ci.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
+
+	if(inf.m_minMagFilter == SamplingFilter::kNearest)
+	{
+		ci.minFilter = VK_FILTER_NEAREST;
+	}
+	else if(inf.m_minMagFilter == SamplingFilter::kLinear)
+	{
+		ci.minFilter = VK_FILTER_LINEAR;
+	}
+	else
+	{
+		ANKI_ASSERT(inf.m_minMagFilter == SamplingFilter::kMax || inf.m_minMagFilter == SamplingFilter::kMin);
+		ANKI_ASSERT(getGrManagerImpl().getDeviceCapabilities().m_samplingFilterMinMax);
+		ci.minFilter = VK_FILTER_LINEAR;
+	}
+
+	ci.magFilter = ci.minFilter;
+
+	if(inf.m_mipmapFilter == SamplingFilter::kBase || inf.m_mipmapFilter == SamplingFilter::kNearest)
+	{
+		ci.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
+	}
+	else
+	{
+		ci.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
+	}
+
+	switch(inf.m_addressing)
+	{
+	case SamplingAddressing::kClamp:
+		ci.addressModeU = ci.addressModeV = ci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+		break;
+	case SamplingAddressing::kRepeat:
+		ci.addressModeU = ci.addressModeV = ci.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
+		break;
+	case SamplingAddressing::kBlack:
+		ci.addressModeU = ci.addressModeV = ci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
+		ci.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
+		break;
+	case SamplingAddressing::kWhite:
+		ci.addressModeU = ci.addressModeV = ci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
+		ci.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
+		break;
+	default:
+		ANKI_ASSERT(0);
+	}
+
+	ci.mipLodBias = inf.m_lodBias;
+
+	if(inf.m_anisotropyLevel > 0)
+	{
+		ci.anisotropyEnable = VK_TRUE;
+		ci.maxAnisotropy = inf.m_anisotropyLevel;
+	}
+
+	ci.compareOp = convertCompareOp(inf.m_compareOperation);
+	if(ci.compareOp != VK_COMPARE_OP_ALWAYS)
+	{
+		ci.compareEnable = VK_TRUE;
+	}
+
+	ci.minLod = inf.m_minLod;
+	ci.maxLod = inf.m_maxLod;
+
+	ci.unnormalizedCoordinates = VK_FALSE;
+
+	// min/max
+	VkSamplerReductionModeCreateInfoEXT reductionCi = {};
+	if(inf.m_minMagFilter == SamplingFilter::kMax || inf.m_minMagFilter == SamplingFilter::kMin)
+	{
+		reductionCi.sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT;
+		if(inf.m_minMagFilter == SamplingFilter::kMax)
+		{
+			reductionCi.reductionMode = VK_SAMPLER_REDUCTION_MODE_MAX_EXT;
+		}
+		else
+		{
+			ANKI_ASSERT(inf.m_minMagFilter == SamplingFilter::kMin);
+			reductionCi.reductionMode = VK_SAMPLER_REDUCTION_MODE_MIN_EXT;
+		}
+
+		ci.pNext = &reductionCi;
+	}
+
+	// Create
+	ANKI_VK_CHECK(vkCreateSampler(getVkDevice(), &ci, nullptr, &m_handle));
+	getGrManagerImpl().trySetVulkanHandleName(inf.getName(), VK_OBJECT_TYPE_SAMPLER, m_handle);
+
+	return Error::kNone;
+}
+
+void SamplerFactory::destroy()
+{
+	for(auto it : m_map)
+	{
+		MicroSampler* const sampler = it;
+		ANKI_ASSERT(sampler->getRefcount() == 0 && "Someone still holds a reference to a sampler");
+		deleteInstance(GrMemoryPool::getSingleton(), sampler);
+	}
+
+	m_map.destroy();
+}
+
+Error SamplerFactory::newInstance(const SamplerInitInfo& inf, MicroSamplerPtr& psampler)
+{
+	Error err = Error::kNone;
+	MicroSampler* out = nullptr;
+	const U64 hash = inf.computeHash();
+
+	LockGuard<Mutex> lock(m_mtx);
+
+	auto it = m_map.find(hash);
+
+	if(it != m_map.getEnd())
+	{
+		out = *it;
+	}
+	else
+	{
+		// Create a new one
+
+		out = anki::newInstance<MicroSampler>(GrMemoryPool::getSingleton());
+		err = out->init(inf);
+
+		if(err)
+		{
+			deleteInstance(GrMemoryPool::getSingleton(), out);
+			out = nullptr;
+		}
+		else
+		{
+			m_map.emplace(hash, out);
+		}
+	}
+
+	if(!err)
+	{
+		psampler.reset(out);
+	}
+
+	return err;
+}
+
 Sampler* Sampler::newInstance(const SamplerInitInfo& init)
 {
 	SamplerImpl* impl = anki::newInstance<SamplerImpl>(GrMemoryPool::getSingleton(), init.getName());
-	const Error err = getGrManagerImpl().getSamplerFactory().newInstance(init, impl->m_sampler);
+	const Error err = SamplerFactory::getSingleton().newInstance(init, impl->m_sampler);
 	if(err)
 	{
 		deleteInstance(GrMemoryPool::getSingleton(), impl);

+ 77 - 1
AnKi/Gr/Vulkan/VkSampler.h

@@ -6,13 +6,89 @@
 #pragma once
 
 #include <AnKi/Gr/Sampler.h>
-#include <AnKi/Gr/Vulkan/VkSamplerFactory.h>
+#include <AnKi/Gr/Vulkan/VkCommon.h>
+#include <AnKi/Util/HashMap.h>
 
 namespace anki {
 
 /// @addtogroup vulkan
 /// @{
 
+/// A thin wapper over VkSampler.
+class MicroSampler
+{
+	friend class MicroSamplerPtrDeleter;
+	friend class SamplerFactory;
+	ANKI_FRIEND_CALL_CONSTRUCTOR_AND_DESTRUCTOR
+
+public:
+	const VkSampler& getHandle() const
+	{
+		ANKI_ASSERT(m_handle);
+		return m_handle;
+	}
+
+	void retain() const
+	{
+		m_refcount.fetchAdd(1);
+	}
+
+	I32 release() const
+	{
+		return m_refcount.fetchSub(1);
+	}
+
+	I32 getRefcount() const
+	{
+		return m_refcount.load();
+	}
+
+private:
+	VkSampler m_handle = VK_NULL_HANDLE;
+	mutable Atomic<I32> m_refcount = {0};
+
+	MicroSampler() = default;
+
+	~MicroSampler();
+
+	Error init(const SamplerInitInfo& inf);
+};
+
+/// MicroSamplerPtr deleter.
+class MicroSamplerPtrDeleter
+{
+public:
+	void operator()([[maybe_unused]] MicroSampler* s)
+	{
+		ANKI_ASSERT(s);
+		// Do nothing. The samplers will be destroyed at app shutdown
+	}
+};
+
+/// MicroSampler smart pointer.
+using MicroSamplerPtr = IntrusivePtr<MicroSampler, MicroSamplerPtrDeleter>;
+
+/// Sampler factory. Used to avoid creating too many duplicate samplers.
+class SamplerFactory : public MakeSingleton<SamplerFactory>
+{
+	friend class MicroSampler;
+
+public:
+	~SamplerFactory()
+	{
+		destroy();
+	}
+
+	/// Create a new sampler. It's thread-safe.
+	Error newInstance(const SamplerInitInfo& inf, MicroSamplerPtr& psampler);
+
+private:
+	GrHashMap<U64, MicroSampler*> m_map;
+	Mutex m_mtx;
+
+	void destroy();
+};
+
 /// Vulkan implementation of Sampler.
 class SamplerImpl final : public Sampler
 {

+ 0 - 167
AnKi/Gr/Vulkan/VkSamplerFactory.cpp

@@ -1,167 +0,0 @@
-// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Gr/Vulkan/VkSamplerFactory.h>
-#include <AnKi/Gr/Vulkan/VkGrManager.h>
-
-namespace anki {
-
-MicroSampler::~MicroSampler()
-{
-	if(m_handle)
-	{
-		vkDestroySampler(getVkDevice(), m_handle, nullptr);
-	}
-}
-
-Error MicroSampler::init(const SamplerInitInfo& inf)
-{
-	// Fill the create cio
-	VkSamplerCreateInfo ci = {};
-	ci.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
-
-	if(inf.m_minMagFilter == SamplingFilter::kNearest)
-	{
-		ci.minFilter = VK_FILTER_NEAREST;
-	}
-	else if(inf.m_minMagFilter == SamplingFilter::kLinear)
-	{
-		ci.minFilter = VK_FILTER_LINEAR;
-	}
-	else
-	{
-		ANKI_ASSERT(inf.m_minMagFilter == SamplingFilter::kMax || inf.m_minMagFilter == SamplingFilter::kMin);
-		ANKI_ASSERT(getGrManagerImpl().getDeviceCapabilities().m_samplingFilterMinMax);
-		ci.minFilter = VK_FILTER_LINEAR;
-	}
-
-	ci.magFilter = ci.minFilter;
-
-	if(inf.m_mipmapFilter == SamplingFilter::kBase || inf.m_mipmapFilter == SamplingFilter::kNearest)
-	{
-		ci.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
-	}
-	else
-	{
-		ci.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
-	}
-
-	switch(inf.m_addressing)
-	{
-	case SamplingAddressing::kClamp:
-		ci.addressModeU = ci.addressModeV = ci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
-		break;
-	case SamplingAddressing::kRepeat:
-		ci.addressModeU = ci.addressModeV = ci.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;
-		break;
-	case SamplingAddressing::kBlack:
-		ci.addressModeU = ci.addressModeV = ci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
-		ci.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
-		break;
-	case SamplingAddressing::kWhite:
-		ci.addressModeU = ci.addressModeV = ci.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
-		ci.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
-		break;
-	default:
-		ANKI_ASSERT(0);
-	}
-
-	ci.mipLodBias = inf.m_lodBias;
-
-	if(inf.m_anisotropyLevel > 0)
-	{
-		ci.anisotropyEnable = VK_TRUE;
-		ci.maxAnisotropy = inf.m_anisotropyLevel;
-	}
-
-	ci.compareOp = convertCompareOp(inf.m_compareOperation);
-	if(ci.compareOp != VK_COMPARE_OP_ALWAYS)
-	{
-		ci.compareEnable = VK_TRUE;
-	}
-
-	ci.minLod = inf.m_minLod;
-	ci.maxLod = inf.m_maxLod;
-
-	ci.unnormalizedCoordinates = VK_FALSE;
-
-	// min/max
-	VkSamplerReductionModeCreateInfoEXT reductionCi = {};
-	if(inf.m_minMagFilter == SamplingFilter::kMax || inf.m_minMagFilter == SamplingFilter::kMin)
-	{
-		reductionCi.sType = VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT;
-		if(inf.m_minMagFilter == SamplingFilter::kMax)
-		{
-			reductionCi.reductionMode = VK_SAMPLER_REDUCTION_MODE_MAX_EXT;
-		}
-		else
-		{
-			ANKI_ASSERT(inf.m_minMagFilter == SamplingFilter::kMin);
-			reductionCi.reductionMode = VK_SAMPLER_REDUCTION_MODE_MIN_EXT;
-		}
-
-		ci.pNext = &reductionCi;
-	}
-
-	// Create
-	ANKI_VK_CHECK(vkCreateSampler(getVkDevice(), &ci, nullptr, &m_handle));
-	getGrManagerImpl().trySetVulkanHandleName(inf.getName(), VK_OBJECT_TYPE_SAMPLER, m_handle);
-
-	return Error::kNone;
-}
-
-void SamplerFactory::destroy()
-{
-	for(auto it : m_map)
-	{
-		MicroSampler* const sampler = it;
-		ANKI_ASSERT(sampler->getRefcount() == 0 && "Someone still holds a reference to a sampler");
-		deleteInstance(GrMemoryPool::getSingleton(), sampler);
-	}
-
-	m_map.destroy();
-}
-
-Error SamplerFactory::newInstance(const SamplerInitInfo& inf, MicroSamplerPtr& psampler)
-{
-	Error err = Error::kNone;
-	MicroSampler* out = nullptr;
-	const U64 hash = inf.computeHash();
-
-	LockGuard<Mutex> lock(m_mtx);
-
-	auto it = m_map.find(hash);
-
-	if(it != m_map.getEnd())
-	{
-		out = *it;
-	}
-	else
-	{
-		// Create a new one
-
-		out = anki::newInstance<MicroSampler>(GrMemoryPool::getSingleton(), this);
-		err = out->init(inf);
-
-		if(err)
-		{
-			deleteInstance(GrMemoryPool::getSingleton(), out);
-			out = nullptr;
-		}
-		else
-		{
-			m_map.emplace(hash, out);
-		}
-	}
-
-	if(!err)
-	{
-		psampler.reset(out);
-	}
-
-	return err;
-}
-
-} // end namespace anki

+ 0 - 95
AnKi/Gr/Vulkan/VkSamplerFactory.h

@@ -1,95 +0,0 @@
-// Copyright (C) 2009-2023, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <AnKi/Gr/Vulkan/VkFenceFactory.h>
-#include <AnKi/Util/HashMap.h>
-
-namespace anki {
-
-// Forward
-class SamplerFactory;
-
-/// @addtogroup vulkan
-/// @{
-
-/// A thin wapper over VkSampler.
-class MicroSampler
-{
-	friend class MicroSamplerPtrDeleter;
-	friend class SamplerFactory;
-	ANKI_FRIEND_CALL_CONSTRUCTOR_AND_DESTRUCTOR
-
-public:
-	const VkSampler& getHandle() const
-	{
-		ANKI_ASSERT(m_handle);
-		return m_handle;
-	}
-
-	void retain() const
-	{
-		m_refcount.fetchAdd(1);
-	}
-
-	I32 release() const
-	{
-		return m_refcount.fetchSub(1);
-	}
-
-	I32 getRefcount() const
-	{
-		return m_refcount.load();
-	}
-
-private:
-	VkSampler m_handle = VK_NULL_HANDLE;
-	mutable Atomic<I32> m_refcount = {0};
-	SamplerFactory* m_factory = nullptr;
-
-	MicroSampler(SamplerFactory* f)
-		: m_factory(f)
-	{
-		ANKI_ASSERT(f);
-	}
-
-	~MicroSampler();
-
-	Error init(const SamplerInitInfo& inf);
-};
-
-/// MicroSamplerPtr deleter.
-class MicroSamplerPtrDeleter
-{
-public:
-	void operator()([[maybe_unused]] MicroSampler* s)
-	{
-		ANKI_ASSERT(s);
-		// Do nothing. The samplers will be destroyed at app shutdown
-	}
-};
-
-/// MicroSampler smart pointer.
-using MicroSamplerPtr = IntrusivePtr<MicroSampler, MicroSamplerPtrDeleter>;
-
-/// Sampler factory. Used to avoid creating too many duplicate samplers.
-class SamplerFactory
-{
-	friend class MicroSampler;
-
-public:
-	void destroy();
-
-	/// Create a new sampler. It's thread-safe.
-	Error newInstance(const SamplerInitInfo& inf, MicroSamplerPtr& psampler);
-
-private:
-	GrHashMap<U64, MicroSampler*> m_map;
-	Mutex m_mtx;
-};
-/// @}
-
-} // end namespace anki

+ 9 - 11
AnKi/Gr/Vulkan/VkSemaphoreFactory.inl.h → AnKi/Gr/Vulkan/VkSemaphoreFactory.cpp

@@ -8,12 +8,10 @@
 
 namespace anki {
 
-inline MicroSemaphore::MicroSemaphore(SemaphoreFactory* f, MicroFencePtr fence, Bool isTimeline)
-	: m_factory(f)
-	, m_fence(fence)
+MicroSemaphore::MicroSemaphore(MicroFencePtr fence, Bool isTimeline)
+	: m_fence(fence)
 	, m_isTimeline(isTimeline)
 {
-	ANKI_ASSERT(f);
 	ANKI_ASSERT(fence.isCreated());
 
 	VkSemaphoreTypeCreateInfo typeCreateInfo = {};
@@ -29,7 +27,7 @@ inline MicroSemaphore::MicroSemaphore(SemaphoreFactory* f, MicroFencePtr fence,
 	ANKI_VK_CHECKF(vkCreateSemaphore(getVkDevice(), &ci, nullptr, &m_handle));
 }
 
-inline MicroSemaphore::~MicroSemaphore()
+MicroSemaphore::~MicroSemaphore()
 {
 	if(m_handle)
 	{
@@ -37,7 +35,7 @@ inline MicroSemaphore::~MicroSemaphore()
 	}
 }
 
-inline Bool MicroSemaphore::clientWait(Second seconds)
+Bool MicroSemaphore::clientWait(Second seconds)
 {
 	ANKI_ASSERT(m_isTimeline);
 
@@ -59,20 +57,20 @@ inline Bool MicroSemaphore::clientWait(Second seconds)
 	return res != VK_TIMEOUT;
 }
 
-inline void MicroSemaphorePtrDeleter::operator()(MicroSemaphore* s)
+void MicroSemaphorePtrDeleter::operator()(MicroSemaphore* s)
 {
 	ANKI_ASSERT(s);
 	if(s->m_isTimeline)
 	{
-		s->m_factory->m_timelineRecycler.recycle(s);
+		SemaphoreFactory::getSingleton().m_timelineRecycler.recycle(s);
 	}
 	else
 	{
-		s->m_factory->m_binaryRecycler.recycle(s);
+		SemaphoreFactory::getSingleton().m_binaryRecycler.recycle(s);
 	}
 }
 
-inline MicroSemaphorePtr SemaphoreFactory::newInstance(MicroFencePtr fence, Bool isTimeline)
+MicroSemaphorePtr SemaphoreFactory::newInstance(MicroFencePtr fence, Bool isTimeline)
 {
 	ANKI_ASSERT(fence);
 
@@ -81,7 +79,7 @@ inline MicroSemaphorePtr SemaphoreFactory::newInstance(MicroFencePtr fence, Bool
 	if(out == nullptr)
 	{
 		// Create a new one
-		out = anki::newInstance<MicroSemaphore>(GrMemoryPool::getSingleton(), this, fence, isTimeline);
+		out = anki::newInstance<MicroSemaphore>(GrMemoryPool::getSingleton(), fence, isTimeline);
 	}
 	else
 	{

+ 3 - 9
AnKi/Gr/Vulkan/VkSemaphoreFactory.h

@@ -10,9 +10,6 @@
 
 namespace anki {
 
-// Forward
-class SemaphoreFactory;
-
 /// @addtogroup vulkan
 /// @{
 
@@ -91,7 +88,6 @@ public:
 private:
 	VkSemaphore m_handle = VK_NULL_HANDLE;
 	mutable Atomic<I32> m_refcount = {0};
-	SemaphoreFactory* m_factory = nullptr;
 
 	/// Fence to find out when it's safe to reuse this semaphore.
 	MicroFencePtr m_fence;
@@ -99,7 +95,7 @@ private:
 	Atomic<U64> m_timelineValue = {0};
 	Bool m_isTimeline = false;
 
-	MicroSemaphore(SemaphoreFactory* f, MicroFencePtr fence, Bool isTimeline);
+	MicroSemaphore(MicroFencePtr fence, Bool isTimeline);
 
 	~MicroSemaphore();
 };
@@ -115,13 +111,13 @@ public:
 using MicroSemaphorePtr = IntrusivePtr<MicroSemaphore, MicroSemaphorePtrDeleter>;
 
 /// Factory of semaphores.
-class SemaphoreFactory
+class SemaphoreFactory : public MakeSingleton<SemaphoreFactory>
 {
 	friend class MicroSemaphore;
 	friend class MicroSemaphorePtrDeleter;
 
 public:
-	void destroy()
+	~SemaphoreFactory()
 	{
 		m_binaryRecycler.destroy();
 		m_timelineRecycler.destroy();
@@ -136,5 +132,3 @@ private:
 /// @}
 
 } // end namespace anki
-
-#include <AnKi/Gr/Vulkan/VkSemaphoreFactory.inl.h>

+ 2 - 8
AnKi/Gr/Vulkan/VkShaderProgram.cpp

@@ -216,12 +216,6 @@ Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 	if(graphicsProg)
 	{
 		m_graphics.m_pplineFactory = anki::newInstance<PipelineFactory>(GrMemoryPool::getSingleton());
-		m_graphics.m_pplineFactory->init(getGrManagerImpl().getPipelineCache()
-#if ANKI_PLATFORM_MOBILE
-											 ,
-										 getGrManagerImpl().getGlobalCreatePipelineMutex()
-#endif
-		);
 	}
 
 	// Create the pipeline if compute
@@ -246,7 +240,7 @@ Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 		ci.stage.module = shaderImpl.m_handle;
 
 		ANKI_TRACE_SCOPED_EVENT(VkPipelineCreate);
-		ANKI_VK_CHECK(vkCreateComputePipelines(getVkDevice(), getGrManagerImpl().getPipelineCache(), 1, &ci, nullptr, &m_compute.m_ppline));
+		ANKI_VK_CHECK(vkCreateComputePipelines(getVkDevice(), PipelineCache::getSingleton().m_cacheHandle, 1, &ci, nullptr, &m_compute.m_ppline));
 		getGrManagerImpl().printPipelineShaderInfo(m_compute.m_ppline, getName());
 	}
 
@@ -330,7 +324,7 @@ Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 
 		{
 			ANKI_TRACE_SCOPED_EVENT(VkPipelineCreate);
-			ANKI_VK_CHECK(vkCreateRayTracingPipelinesKHR(getVkDevice(), VK_NULL_HANDLE, getGrManagerImpl().getPipelineCache(), 1, &ci, nullptr,
+			ANKI_VK_CHECK(vkCreateRayTracingPipelinesKHR(getVkDevice(), VK_NULL_HANDLE, PipelineCache::getSingleton().m_cacheHandle, 1, &ci, nullptr,
 														 &m_rt.m_ppline));
 		}
 

+ 5 - 7
AnKi/Gr/Vulkan/VkSwapchainFactory.cpp

@@ -5,14 +5,12 @@
 
 #include <AnKi/Gr/Vulkan/VkSwapchainFactory.h>
 #include <AnKi/Gr/Vulkan/VkGrManager.h>
+#include <AnKi/Gr/Vulkan/VkTexture.h>
 
 namespace anki {
 
-MicroSwapchain::MicroSwapchain(SwapchainFactory* factory)
-	: m_factory(factory)
+MicroSwapchain::MicroSwapchain()
 {
-	ANKI_ASSERT(factory);
-
 	if(initInternal())
 	{
 		ANKI_VK_LOGF("Error creating the swapchain. Will not try to recover");
@@ -122,7 +120,7 @@ Error MicroSwapchain::initInternal()
 		Array<VkPresentModeKHR, 4> presentModes;
 		vkGetPhysicalDeviceSurfacePresentModesKHR(pdev, getGrManagerImpl().getSurface(), &presentModeCount, &presentModes[0]);
 
-		if(m_factory->m_vsync)
+		if(SwapchainFactory::getSingleton().m_vsync)
 		{
 			for(U i = 0; i < presentModeCount; ++i)
 			{
@@ -218,7 +216,7 @@ Error MicroSwapchain::initInternal()
 		m_textures.resize(count);
 
 		ANKI_VK_LOGI("Created a swapchain. Image count: %u, present mode: %u, size: %ux%u, vsync: %u", count, presentMode, surfaceWidth,
-					 surfaceHeight, U32(m_factory->m_vsync));
+					 surfaceHeight, U32(SwapchainFactory::getSingleton().m_vsync));
 
 		Array<VkImage, 64> images;
 		ANKI_ASSERT(count <= 64);
@@ -251,7 +249,7 @@ MicroSwapchainPtr SwapchainFactory::newInstance()
 	[[maybe_unused]] MicroSwapchain* dummy = m_recycler.findToReuse();
 	ANKI_ASSERT(dummy == nullptr);
 
-	return MicroSwapchainPtr(anki::newInstance<MicroSwapchain>(GrMemoryPool::getSingleton(), this));
+	return MicroSwapchainPtr(anki::newInstance<MicroSwapchain>(GrMemoryPool::getSingleton()));
 }
 
 } // end namespace anki

+ 6 - 23
AnKi/Gr/Vulkan/VkSwapchainFactory.h

@@ -28,7 +28,7 @@ public:
 
 	GrDynamicArray<TexturePtr> m_textures;
 
-	MicroSwapchain(SwapchainFactory* factory);
+	MicroSwapchain();
 
 	~MicroSwapchain();
 
@@ -63,26 +63,9 @@ public:
 		// Do nothing
 	}
 
-	VkRenderPass getRenderPass(VkAttachmentLoadOp loadOp) const
-	{
-		const U idx = (loadOp == VK_ATTACHMENT_LOAD_OP_DONT_CARE) ? kLoadOpDontCare : kLoadOpClear;
-		return m_rpasses[idx];
-	}
-
 private:
-	mutable Atomic<I32> m_refcount = {0};
-	SwapchainFactory* m_factory = nullptr;
-
-	enum
-	{
-		kLoadOpClear,
-		kLoadOpDontCare,
-		kLoadOpCount
-	};
-
-	Array<VkRenderPass, kLoadOpCount> m_rpasses = {};
-
 	MicroFencePtr m_fence;
+	mutable Atomic<I32> m_refcount = {0};
 
 	Error initInternal();
 };
@@ -98,18 +81,18 @@ public:
 using MicroSwapchainPtr = IntrusivePtr<MicroSwapchain, MicroSwapchainPtrDeleter>;
 
 /// Swapchain factory.
-class SwapchainFactory
+class SwapchainFactory : public MakeSingleton<SwapchainFactory>
 {
 	friend class MicroSwapchainPtrDeleter;
 	friend class MicroSwapchain;
 
 public:
-	void init(Bool vsync)
+	SwapchainFactory(Bool vsync)
 	{
 		m_vsync = vsync;
 	}
 
-	void destroy()
+	~SwapchainFactory()
 	{
 		m_recycler.destroy();
 	}
@@ -125,7 +108,7 @@ private:
 inline void MicroSwapchainPtrDeleter::operator()(MicroSwapchain* s)
 {
 	ANKI_ASSERT(s);
-	s->m_factory->m_recycler.recycle(s);
+	SwapchainFactory::getSingleton().m_recycler.recycle(s);
 }
 
 } // end namespace anki

+ 8 - 7
AnKi/Gr/Vulkan/VkTexture.cpp

@@ -5,6 +5,7 @@
 
 #include <AnKi/Gr/Vulkan/VkTexture.h>
 #include <AnKi/Gr/Vulkan/VkGrManager.h>
+#include <AnKi/Gr/Vulkan/VkDescriptorSet.h>
 
 namespace anki {
 
@@ -310,14 +311,14 @@ Error TextureImpl::initImage(const TextureInitInfo& init)
 
 	vkGetImageMemoryRequirements2(getVkDevice(), &imageRequirementsInfo, &requirements);
 
-	U32 memIdx = getGrManagerImpl().getGpuMemoryManager().findMemoryType(requirements.memoryRequirements.memoryTypeBits,
-																		 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
+	U32 memIdx = GpuMemoryManager::getSingleton().findMemoryType(requirements.memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
+																 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
 
 	// Fallback
 	if(memIdx == kMaxU32)
 	{
-		memIdx = getGrManagerImpl().getGpuMemoryManager().findMemoryType(requirements.memoryRequirements.memoryTypeBits,
-																		 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0);
+		memIdx =
+			GpuMemoryManager::getSingleton().findMemoryType(requirements.memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, 0);
 	}
 
 	ANKI_ASSERT(memIdx != kMaxU32);
@@ -325,12 +326,12 @@ Error TextureImpl::initImage(const TextureInitInfo& init)
 	// Allocate
 	if(!dedicatedRequirements.prefersDedicatedAllocation)
 	{
-		getGrManagerImpl().getGpuMemoryManager().allocateMemory(memIdx, requirements.memoryRequirements.size,
-																U32(requirements.memoryRequirements.alignment), m_memHandle);
+		GpuMemoryManager::getSingleton().allocateMemory(memIdx, requirements.memoryRequirements.size, U32(requirements.memoryRequirements.alignment),
+														m_memHandle);
 	}
 	else
 	{
-		getGrManagerImpl().getGpuMemoryManager().allocateMemoryDedicated(memIdx, requirements.memoryRequirements.size, m_imageHandle, m_memHandle);
+		GpuMemoryManager::getSingleton().allocateMemoryDedicated(memIdx, requirements.memoryRequirements.size, m_imageHandle, m_memHandle);
 	}
 
 	// Bind

+ 2 - 2
AnKi/Gr/Vulkan/VkTimestampQuery.cpp

@@ -55,13 +55,13 @@ TimestampQueryImpl::~TimestampQueryImpl()
 {
 	if(m_handle)
 	{
-		getGrManagerImpl().getTimestampQueryFactory().deleteQuery(m_handle);
+		TimestampQueryFactory::getSingleton().deleteQuery(m_handle);
 	}
 }
 
 Error TimestampQueryImpl::init()
 {
-	ANKI_CHECK(getGrManagerImpl().getTimestampQueryFactory().newQuery(m_handle));
+	ANKI_CHECK(TimestampQueryFactory::getSingleton().newQuery(m_handle));
 
 	m_timestampPeriod = U64(getGrManagerImpl().getPhysicalDeviceProperties().limits.timestampPeriod);