Browse Source

Refactor lifetime

Panagiotis Christopoulos Charitos 5 months ago
parent
commit
0d778866a1
34 changed files with 346 additions and 765 deletions
  1. 30 13
      AnKi/Core/App.cpp
  2. 16 6
      AnKi/Core/App.h
  3. 1 1
      AnKi/Gr/AccelerationStructure.h
  4. 0 107
      AnKi/Gr/BackendCommon/FrameGarbageCollector.h
  5. 11 0
      AnKi/Gr/BackendCommon/InstantiationMacros.def.h
  6. 9 4
      AnKi/Gr/BackendCommon/MicroFenceFactory.h
  7. 7 7
      AnKi/Gr/BackendCommon/MicroFenceFactory.inl.h
  8. 6 17
      AnKi/Gr/BackendCommon/MicroObjectRecycler.h
  9. 30 110
      AnKi/Gr/BackendCommon/MicroObjectRecycler.inl.h
  10. 1 2
      AnKi/Gr/CMakeLists.txt
  11. 0 5
      AnKi/Gr/Common.cpp
  12. 5 23
      AnKi/Gr/Common.h
  13. 1 4
      AnKi/Gr/Vulkan/VkAccelerationStructure.cpp
  14. 5 5
      AnKi/Gr/Vulkan/VkAccelerationStructure.h
  15. 15 19
      AnKi/Gr/Vulkan/VkBuffer.cpp
  16. 3 19
      AnKi/Gr/Vulkan/VkCommandBuffer.cpp
  17. 1 6
      AnKi/Gr/Vulkan/VkCommandBuffer.h
  18. 5 38
      AnKi/Gr/Vulkan/VkCommandBufferFactory.cpp
  19. 9 73
      AnKi/Gr/Vulkan/VkCommandBufferFactory.h
  20. 10 0
      AnKi/Gr/Vulkan/VkCommon.cpp
  21. 11 0
      AnKi/Gr/Vulkan/VkCommon.h
  22. 0 62
      AnKi/Gr/Vulkan/VkFrameGarbageCollector.cpp
  23. 0 57
      AnKi/Gr/Vulkan/VkFrameGarbageCollector.h
  24. 57 65
      AnKi/Gr/Vulkan/VkGrManager.cpp
  25. 49 15
      AnKi/Gr/Vulkan/VkGrManager.h
  26. 16 23
      AnKi/Gr/Vulkan/VkSemaphoreFactory.cpp
  27. 10 31
      AnKi/Gr/Vulkan/VkSemaphoreFactory.h
  28. 2 2
      AnKi/Gr/Vulkan/VkShaderProgram.cpp
  29. 2 2
      AnKi/Gr/Vulkan/VkShaderProgram.h
  30. 11 1
      AnKi/Gr/Vulkan/VkSwapchainFactory.cpp
  31. 12 35
      AnKi/Gr/Vulkan/VkSwapchainFactory.h
  32. 9 11
      AnKi/Gr/Vulkan/VkTexture.cpp
  33. 1 1
      Samples/Common/SampleApp.h
  34. 1 1
      Samples/Sponza/Main.cpp

+ 30 - 13
AnKi/Core/App.cpp

@@ -111,10 +111,18 @@ void* App::statsAllocCallback(void* userData, void* ptr, PtrSize size, [[maybe_u
 	return out;
 }
 
-App::App(AllocAlignedCallback allocCb, void* allocCbUserData)
+App::App(U32 argc, Char** argv, CString appName, AllocAlignedCallback allocCb, void* allocCbUserData)
 {
 	m_originalAllocCallback = allocCb;
 	m_originalAllocUserData = allocCbUserData;
+
+	if(!appName.isEmpty())
+	{
+		appName = "unnamed app";
+	}
+
+	m_appName = static_cast<Char*>(allocCb(allocCbUserData, nullptr, appName.getLength() + 1, alignof(Char)));
+	strcpy(m_appName, appName.cstr());
 }
 
 App::~App()
@@ -155,22 +163,12 @@ void App::cleanup()
 	CoreMemoryPool::freeSingleton();
 	DefaultMemoryPool::freeSingleton();
 
+	m_originalAllocCallback(m_originalAllocUserData, m_appName, 0, 0);
+
 	ANKI_CORE_LOGI("Application finished shutting down");
 }
 
 Error App::init()
-{
-	const Error err = initInternal();
-	if(err)
-	{
-		ANKI_CORE_LOGE("App initialization failed. Shutting down");
-		cleanup();
-	}
-
-	return err;
-}
-
-Error App::initInternal()
 {
 	StatsSet::getSingleton().initFromMainThread();
 	Logger::getSingleton().enableVerbosity(g_verboseLogCVar);
@@ -367,6 +365,25 @@ Error App::initDirs()
 
 Error App::mainLoop()
 {
+	// Initialize the application
+	Error err = Error::kNone;
+	if((err = init()))
+	{
+		ANKI_CORE_LOGE("App initialization failed. Shutting down");
+		cleanup();
+		return err;
+	}
+
+	GrManager::getSingleton().beginFrame();
+	if((err = userInit()))
+	{
+		ANKI_CORE_LOGE("User initialization failed. Shutting down");
+		cleanup();
+		return err;
+	}
+	GrManager::getSingleton().endFrame();
+
+	// Continue with the main loop
 	ANKI_CORE_LOGI("Entering main loop");
 	Bool quit = false;
 

+ 16 - 6
AnKi/Core/App.h

@@ -39,13 +39,10 @@ inline StatCounter g_cpuTotalTimeStatVar(StatCategory::kTime, "CPU total",
 class App
 {
 public:
-	App(AllocAlignedCallback allocCb = allocAligned, void* allocCbUserData = nullptr);
+	App(U32 argc, Char** argv, CString applicationName, AllocAlignedCallback allocCb = allocAligned, void* allocCbUserData = nullptr);
 
 	virtual ~App();
 
-	/// Initialize the application.
-	Error init();
-
 	CString getSettingsDirectory() const
 	{
 		return m_settingsDir;
@@ -59,7 +56,14 @@ public:
 	/// Run the main loop.
 	Error mainLoop();
 
-	/// The user code to run along with the other main loop code.
+	/// User defined init code that will execute after all subsystems have initialized.
+	virtual Error userInit()
+	{
+		// Do nothing
+		return Error::kNone;
+	}
+
+	/// User defined code to run along with the other main loop code.
 	virtual Error userMainLoop([[maybe_unused]] Bool& quit, [[maybe_unused]] Second elapsedTime)
 	{
 		// Do nothing
@@ -73,10 +77,16 @@ public:
 		return m_consoleEnabled;
 	}
 
+	CString getApplicationName() const
+	{
+		return m_appName;
+	}
+
 private:
 	Bool m_consoleEnabled = false;
 	CoreString m_settingsDir; ///< The path that holds the configuration
 	CoreString m_cacheDir; ///< This is used as a cache
+	Char* m_appName = nullptr;
 
 	void* m_originalAllocUserData = nullptr;
 	AllocAlignedCallback m_originalAllocCallback = nullptr;
@@ -85,7 +95,7 @@ private:
 
 	void initMemoryCallbacks(AllocAlignedCallback& allocCb, void*& allocCbUserData);
 
-	Error initInternal();
+	Error init();
 
 	Error initDirs();
 	void cleanup();

+ 1 - 1
AnKi/Gr/AccelerationStructure.h

@@ -45,7 +45,7 @@ public:
 class AccelerationStructureInstanceInfo
 {
 public:
-	AccelerationStructurePtr m_bottomLevel;
+	AccelerationStructure* m_bottomLevel = nullptr;
 	Mat3x4 m_transform = Mat3x4::getIdentity();
 	U32 m_hitgroupSbtRecordIndex = 0; ///< Points to a hitgroup SBT record.
 	U8 m_mask = 0xFF; ///< A mask that this instance belongs to. Will be tested against what's in traceRayEXT().

+ 0 - 107
AnKi/Gr/BackendCommon/FrameGarbageCollector.h

@@ -1,107 +0,0 @@
-// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#pragma once
-
-#include <AnKi/Gr/BackendCommon/Common.h>
-#include <AnKi/Util/Thread.h>
-
-namespace anki {
-
-/// @addtogroup graphics
-/// @{
-
-/// This class gathers various garbages and disposes them when in some later frame where it is safe to do so. This is used on bindless textures and
-/// buffers where we have to wait until the frame where they were deleted is done.
-template<typename TTextureGarbage, typename TBufferGarbage, typename TASGarbage>
-class FrameGarbageCollector
-{
-public:
-	FrameGarbageCollector() = default;
-
-	~FrameGarbageCollector()
-	{
-		destroy();
-	}
-
-	void destroy()
-	{
-		for(FrameGarbage& frame : m_frames)
-		{
-			collectGarbage(frame);
-		}
-	}
-
-	/// Sets a new frame and collects garbage as well.
-	/// @note It's thread-safe.
-	void beginFrameAndCollectGarbage()
-	{
-		LockGuard lock(m_mtx);
-		m_frame = (m_frame + 1) % m_frames.getSize();
-		FrameGarbage& frame = m_frames[m_frame];
-		collectGarbage(frame);
-	}
-
-	/// @note It's thread-safe.
-	void newTextureGarbage(TTextureGarbage* textureGarbage)
-	{
-		ANKI_ASSERT(textureGarbage);
-		LockGuard lock(m_mtx);
-		FrameGarbage& frame = m_frames[m_frame];
-		frame.m_textureGarbage.pushBack(textureGarbage);
-	}
-
-	/// @note It's thread-safe.
-	void newBufferGarbage(TBufferGarbage* bufferGarbage)
-	{
-		ANKI_ASSERT(bufferGarbage);
-		LockGuard lock(m_mtx);
-		FrameGarbage& frame = m_frames[m_frame];
-		frame.m_bufferGarbage.pushBack(bufferGarbage);
-	}
-
-	/// @note It's thread-safe.
-	void newASGarbage(TASGarbage* garbage)
-	{
-		ANKI_ASSERT(garbage);
-		LockGuard lock(m_mtx);
-		FrameGarbage& frame = m_frames[m_frame];
-		frame.m_asGarbage.pushBack(garbage);
-	}
-
-private:
-	class FrameGarbage
-	{
-	public:
-		IntrusiveList<TTextureGarbage> m_textureGarbage;
-		IntrusiveList<TBufferGarbage> m_bufferGarbage;
-		IntrusiveList<TASGarbage> m_asGarbage;
-	};
-
-	Mutex m_mtx;
-	Array<FrameGarbage, kMaxFramesInFlight> m_frames;
-	U32 m_frame = 0;
-
-	static void collectGarbage(FrameGarbage& frame)
-	{
-		while(!frame.m_textureGarbage.isEmpty())
-		{
-			deleteInstance(GrMemoryPool::getSingleton(), frame.m_textureGarbage.popBack());
-		}
-
-		while(!frame.m_bufferGarbage.isEmpty())
-		{
-			deleteInstance(GrMemoryPool::getSingleton(), frame.m_bufferGarbage.popBack());
-		}
-
-		while(!frame.m_asGarbage.isEmpty())
-		{
-			deleteInstance(GrMemoryPool::getSingleton(), frame.m_asGarbage.popBack());
-		}
-	}
-};
-/// @}
-
-} // end namespace anki

+ 11 - 0
AnKi/Gr/BackendCommon/InstantiationMacros.def.h

@@ -5,6 +5,10 @@
 
 // An awful trick to instantiate stuff with the graphics object type
 
+#if !defined(ANKI_INSTANTIATE_GR_OBJECT_DELIMITER)
+#	define ANKI_INSTANTIATE_GR_OBJECT_DELIMITER()
+#endif
+
 ANKI_INSTANTIATE_GR_OBJECT(Buffer)
 ANKI_INSTANTIATE_GR_OBJECT_DELIMITER()
 ANKI_INSTANTIATE_GR_OBJECT(CommandBuffer)
@@ -26,3 +30,10 @@ ANKI_INSTANTIATE_GR_OBJECT_DELIMITER()
 ANKI_INSTANTIATE_GR_OBJECT(AccelerationStructure)
 ANKI_INSTANTIATE_GR_OBJECT_DELIMITER()
 ANKI_INSTANTIATE_GR_OBJECT(GrUpscaler)
+ANKI_INSTANTIATE_GR_OBJECT_DELIMITER()
+ANKI_INSTANTIATE_GR_OBJECT(PipelineQuery)
+ANKI_INSTANTIATE_GR_OBJECT_DELIMITER()
+ANKI_INSTANTIATE_GR_OBJECT(RenderGraph)
+
+#undef ANKI_INSTANTIATE_GR_OBJECT
+#undef ANKI_INSTANTIATE_GR_OBJECT_DELIMITER

+ 9 - 4
AnKi/Gr/BackendCommon/MicroFenceFactory.h

@@ -100,6 +100,11 @@ public:
 		return bSignaled;
 	}
 
+	CString getName() const
+	{
+		return m_name;
+	}
+
 private:
 	enum State : U8
 	{
@@ -121,7 +126,7 @@ template<typename TImplementation>
 class MicroFenceFactory
 {
 public:
-	using MicroFence = MicroFence<TImplementation>;
+	using MyMicroFence = MicroFence<TImplementation>;
 
 	/// Limit the alive fences to avoid having too many file descriptors used in Linux.
 	static constexpr U32 kMaxAliveFences = 32;
@@ -131,12 +136,12 @@ public:
 	~MicroFenceFactory();
 
 	/// Create a new fence pointer.
-	MicroFence* newFence(CString name = "unnamed");
+	MyMicroFence* newFence(CString name = "unnamed");
 
-	void releaseFence(MicroFence* fence);
+	void releaseFence(MyMicroFence* fence);
 
 private:
-	GrBlockArray<MicroFence> m_fences;
+	GrBlockArray<MyMicroFence> m_fences;
 	U32 m_aliveFenceCount = 0;
 	Mutex m_mtx;
 	BitSet<1024, U64> m_markedForDeletionMask = {false}; // Always last for better caching

+ 7 - 7
AnKi/Gr/BackendCommon/MicroFenceFactory.inl.h

@@ -22,7 +22,7 @@ MicroFenceFactory<TImplementation>::~MicroFenceFactory()
 
 	for(U32 idx : fenceIdxToDelete)
 	{
-		MicroFence& fence = m_fences[idx];
+		MyMicroFence& fence = m_fences[idx];
 
 		if(fence.m_impl)
 		{
@@ -41,9 +41,9 @@ MicroFenceFactory<TImplementation>::~MicroFenceFactory()
 }
 
 template<typename TImplementation>
-MicroFenceFactory<TImplementation>::MicroFence* MicroFenceFactory<TImplementation>::newFence(CString name)
+MicroFenceFactory<TImplementation>::MyMicroFence* MicroFenceFactory<TImplementation>::newFence(CString name)
 {
-	MicroFence* out = nullptr;
+	MyMicroFence* out = nullptr;
 
 	LockGuard<Mutex> lock(m_mtx);
 
@@ -69,7 +69,7 @@ MicroFenceFactory<TImplementation>::MicroFence* MicroFenceFactory<TImplementatio
 
 			// Marked for deletion or signaled will loose their fence
 
-			Bool destroyFence = it->m_state == MicroFence::kSignaled;
+			Bool destroyFence = it->m_state == MyMicroFence::kSignaled;
 			if(!destroyFence)
 			{
 				const Bool signaled = it->m_impl.signaled();
@@ -77,7 +77,7 @@ MicroFenceFactory<TImplementation>::MicroFence* MicroFenceFactory<TImplementatio
 
 				if(signaled)
 				{
-					it->m_state = MicroFence::kSignaled;
+					it->m_state = MyMicroFence::kSignaled;
 				}
 			}
 
@@ -141,7 +141,7 @@ MicroFenceFactory<TImplementation>::MicroFence* MicroFenceFactory<TImplementatio
 		out->m_impl.reset();
 	}
 
-	out->m_state = MicroFence::kUnsignaled;
+	out->m_state = MyMicroFence::kUnsignaled;
 	out->m_impl.setName(name);
 	out->m_name = name;
 
@@ -150,7 +150,7 @@ MicroFenceFactory<TImplementation>::MicroFence* MicroFenceFactory<TImplementatio
 }
 
 template<typename TImplementation>
-void MicroFenceFactory<TImplementation>::releaseFence(MicroFence* fence)
+void MicroFenceFactory<TImplementation>::releaseFence(MyMicroFence* fence)
 {
 	LockGuard lock(m_mtx);
 

+ 6 - 17
AnKi/Gr/BackendCommon/MicroObjectRecycler.h

@@ -40,43 +40,32 @@ public:
 	void trimCache()
 	{
 		LockGuard<Mutex> lock(m_mtx);
-		checkDoneFences();
 		trimCacheInternal(0);
 	}
 
 	U32 getCacheSize() const
 	{
-		return m_objects.getSize();
+		return m_objectCache.getSize();
 	}
 
 private:
-	class Object
-	{
-	public:
-		T* m_microObject;
-		Bool m_fenceDone;
-	};
-
-	GrDynamicArray<Object> m_objects;
+	GrDynamicArray<T*> m_objectCache;
 	Mutex m_mtx;
 
 	// Begin trim cache adjustment vars
-	U32 m_readyObjectsAfterTrim = 1;
-	static constexpr U32 m_maxRequestsPerAdjustment = 128;
+	U32 m_availableObjectsAfterTrim = 1;
+	static constexpr U32 kMaxRequestsPerAdjustment = 128;
 	U32 m_cacheMisses = 0;
 	U32 m_requests = 0;
-	U32 m_minCacheSizePerRequest = kMaxU32;
 	// End trim cache adjustment vars
 
-#if ANKI_EXTRA_CHECKS
-	U32 m_createdAndNotRecycled = 0;
+#if ANKI_ASSERTIONS_ENABLED
+	U32 m_inUseObjects = 0;
 #endif
 
 	void trimCacheInternal(U32 aliveObjectCountAfterTrim);
 
 	void adjustAliveObjectCount();
-
-	void checkDoneFences();
 };
 /// @}
 

+ 30 - 110
AnKi/Gr/BackendCommon/MicroObjectRecycler.inl.h

@@ -12,22 +12,15 @@ inline void MicroObjectRecycler<T>::destroy()
 {
 	LockGuard<Mutex> lock(m_mtx);
 
-	checkDoneFences();
-
-	for(U32 i = 0; i < m_objects.getSize(); ++i)
+	for(U32 i = 0; i < m_objectCache.getSize(); ++i)
 	{
-		T* mobj = m_objects[i].m_microObject;
+		T* mobj = m_objectCache[i];
 		ANKI_ASSERT(mobj);
-		ANKI_ASSERT(!mobj->getFence());
-
 		deleteInstance(GrMemoryPool::getSingleton(), mobj);
-#if ANKI_EXTRA_CHECKS
-		--m_createdAndNotRecycled;
-#endif
 	}
 
-	m_objects.destroy();
-	ANKI_ASSERT(m_createdAndNotRecycled == 0 && "Destroying the recycler while objects have not recycled yet");
+	m_objectCache.destroy();
+	ANKI_ASSERT(m_inUseObjects == 0 && "Destroying the recycler while objects have not recycled yet");
 }
 
 template<typename T>
@@ -36,33 +29,23 @@ inline T* MicroObjectRecycler<T>::findToReuse()
 	T* out = nullptr;
 	LockGuard<Mutex> lock(m_mtx);
 
-	checkDoneFences();
 	adjustAliveObjectCount();
 
 	// Trim the cache but leave at least one object to be recycled
-	trimCacheInternal(max(m_readyObjectsAfterTrim, 1u));
+	trimCacheInternal(max(m_availableObjectsAfterTrim, 1u));
 
-	for(U32 i = 0; i < m_objects.getSize(); ++i)
+	if(m_objectCache.getSize())
 	{
-		if(m_objects[i].m_fenceDone)
-		{
-			out = m_objects[i].m_microObject;
-			m_objects[i] = m_objects[m_objects.getSize() - 1];
-			m_objects.popBack();
-
-			break;
-		}
+		out = m_objectCache[m_objectCache.getSize() - 1];
+		m_objectCache.popBack();
 	}
 
 	ANKI_ASSERT(out == nullptr || out->getRefcount() == 0);
 
 	m_cacheMisses += (out == nullptr);
 
-#if ANKI_EXTRA_CHECKS
-	if(out == nullptr)
-	{
-		++m_createdAndNotRecycled;
-	}
+#if ANKI_ASSERTIONS_ENABLED
+	++m_inUseObjects;
 #endif
 
 	return out;
@@ -76,102 +59,40 @@ void MicroObjectRecycler<T>::recycle(T* mobj)
 
 	LockGuard<Mutex> lock(m_mtx);
 
-	if(!mobj->getFence())
-	{
-		ANKI_GR_LOGW("Object is recycled and doesn't have a fence");
-	}
-
-	Object obj;
-	obj.m_fenceDone = !mobj->getFence();
-	obj.m_microObject = mobj;
-
-	if(obj.m_fenceDone)
-	{
-		mobj->onFenceDone();
-	}
-
-	m_objects.emplaceBack(obj);
-	checkDoneFences();
-	trimCacheInternal(m_readyObjectsAfterTrim);
-}
-
-template<typename T>
-void MicroObjectRecycler<T>::checkDoneFences()
-{
-	for(Object& obj : m_objects)
-	{
-		T& mobj = *obj.m_microObject;
-
-		if(obj.m_fenceDone)
-		{
-			ANKI_ASSERT(!mobj.getFence());
-		}
+	m_objectCache.emplaceBack(mobj);
+	trimCacheInternal(m_availableObjectsAfterTrim);
 
-		if(!obj.m_fenceDone && mobj.getFence() && mobj.getFence()->signaled())
-		{
-			mobj.setFence(nullptr);
-			mobj.onFenceDone();
-			obj.m_fenceDone = true;
-		}
-	}
+#if ANKI_ASSERTIONS_ENABLED
+	ANKI_ASSERT(m_inUseObjects > 0);
+	--m_inUseObjects;
+#endif
 }
 
 template<typename T>
 void MicroObjectRecycler<T>::trimCacheInternal(U32 aliveObjectCountAfterTrim)
 {
-	GrDynamicArray<Object> aliveObjects;
-
-	for(Object& obj : m_objects)
+	aliveObjectCountAfterTrim = min(aliveObjectCountAfterTrim, m_objectCache.getSize());
+	const U32 toBeKilledCount = m_objectCache.getSize() - aliveObjectCountAfterTrim;
+	if(toBeKilledCount == 0)
 	{
-		T& mobj = *obj.m_microObject;
-		const Bool inUseByTheGpu = !obj.m_fenceDone;
-
-		if(inUseByTheGpu)
-		{
-			// Can't delete it for sure
-			aliveObjects.emplaceBack(obj);
-		}
-		else if(aliveObjectCountAfterTrim > 0)
-		{
-			// Need to keep a few alive for recycling
-			aliveObjects.emplaceBack(obj);
-			--aliveObjectCountAfterTrim;
-		}
-		else
-		{
-			deleteInstance(GrMemoryPool::getSingleton(), &mobj);
-#if ANKI_EXTRA_CHECKS
-			--m_createdAndNotRecycled;
-#endif
-		}
+		return;
 	}
 
-	if(aliveObjects.getSize() > 0)
-	{
-		// Some alive, store the alive
-		m_objects.destroy();
-		m_objects = std::move(aliveObjects);
-	}
-	else if(aliveObjects.getSize() == 0 && m_objects.getSize() > 0)
+	for(U32 i = 0; i < toBeKilledCount; ++i)
 	{
-		// All dead, destroy the array
-		m_objects.destroy();
+		deleteInstance(GrMemoryPool::getSingleton(), m_objectCache[i]);
+		m_objectCache[i] = nullptr;
 	}
+
+	m_objectCache.erase(m_objectCache.getBegin(), m_objectCache.getBegin() + toBeKilledCount);
 }
 
 template<typename T>
 void MicroObjectRecycler<T>::adjustAliveObjectCount()
 {
-	U32 readyObjects = 0;
-	for(Object& obj : m_objects)
-	{
-		readyObjects += obj.m_fenceDone;
-	}
-
-	if(m_requests < m_maxRequestsPerAdjustment) [[likely]]
+	if(m_requests < kMaxRequestsPerAdjustment) [[likely]]
 	{
-		// Not enough requests for a recycle
-		m_minCacheSizePerRequest = min(m_minCacheSizePerRequest, readyObjects);
+		// Not enough requests, keep getting stats
 		++m_requests;
 	}
 	else
@@ -179,18 +100,17 @@ void MicroObjectRecycler<T>::adjustAliveObjectCount()
 		if(m_cacheMisses)
 		{
 			// Need more alive objects
-			m_readyObjectsAfterTrim += 4;
+			m_availableObjectsAfterTrim += 4;
 		}
-		else if(m_minCacheSizePerRequest > 2 && m_readyObjectsAfterTrim > 0)
+		else if(m_availableObjectsAfterTrim > 0)
 		{
 			// Have more than enough alive objects per request, decrease alive objects
-			--m_readyObjectsAfterTrim;
+			--m_availableObjectsAfterTrim;
 		}
 
 		// Start new cycle
 		m_cacheMisses = 0;
 		m_requests = 0;
-		m_minCacheSizePerRequest = readyObjects;
 	}
 }
 

+ 1 - 2
AnKi/Gr/CMakeLists.txt

@@ -31,7 +31,6 @@ set(backend_headers
 	GrUpscaler.h
 	Utils/StackGpuMemoryPool.h
 	BackendCommon/Functions.h
-	BackendCommon/InstantiationMacros.def.h
 	BackendCommon/Format.def.h
 	Utils/SegregatedListsGpuMemoryPool.h)
 
@@ -79,7 +78,7 @@ add_library(AnKiGr ${backend_sources} ${backend_headers})
 
 set(COMPILE_DEFS "-DANKI_SOURCE_FILE")
 if(DIRECTX)
-	set(COMPILE_DEFS ${COMPILE_DEFS} "-DUSE_PIX")	
+	set(COMPILE_DEFS ${COMPILE_DEFS} "-DUSE_PIX")
 endif()
 target_compile_definitions(AnKiGr PRIVATE ${COMPILE_DEFS})
 

+ 0 - 5
AnKi/Gr/Common.cpp

@@ -18,11 +18,6 @@ inline constexpr ShaderVariableDataTypeInfo kShaderVariableDataTypeInfos[] = {
 #undef ANKI_SVDT_MACRO_OPAQUE
 };
 
-void GrObjectDeleter::operator()(GrObject* ptr)
-{
-	deleteInstance(GrMemoryPool::getSingleton(), ptr);
-}
-
 const ShaderVariableDataTypeInfo& getShaderVariableDataTypeInfo(ShaderVariableDataType type)
 {
 	ANKI_ASSERT(type > ShaderVariableDataType::kNone && type < ShaderVariableDataType::kCount);

+ 5 - 23
AnKi/Gr/Common.h

@@ -108,32 +108,14 @@ public:
 	void operator()(GrObject* ptr);
 };
 
-/// Smart pointer for resources.
-template<typename T>
-using GrObjectPtrT = IntrusivePtr<T, GrObjectDeleter>;
-
-using GrObjectPtr = GrObjectPtrT<GrObject>;
+// Smart pointer for objects
+using GrObjectPtr = IntrusivePtr<GrObject, GrObjectDeleter>;
 
-#define ANKI_GR_CLASS(x_) \
+#define ANKI_INSTANTIATE_GR_OBJECT(x_) \
 	class x_##Impl; \
 	class x_; \
-	using x_##Ptr = GrObjectPtrT<x_>;
-
-ANKI_GR_CLASS(Buffer)
-ANKI_GR_CLASS(Texture)
-ANKI_GR_CLASS(Sampler)
-ANKI_GR_CLASS(CommandBuffer)
-ANKI_GR_CLASS(Shader)
-ANKI_GR_CLASS(OcclusionQuery)
-ANKI_GR_CLASS(TimestampQuery)
-ANKI_GR_CLASS(PipelineQuery)
-ANKI_GR_CLASS(ShaderProgram)
-ANKI_GR_CLASS(Fence)
-ANKI_GR_CLASS(RenderGraph)
-ANKI_GR_CLASS(AccelerationStructure)
-ANKI_GR_CLASS(GrUpscaler)
-
-#undef ANKI_GR_CLASS
+	using x_##Ptr = IntrusivePtr<x_, GrObjectDeleter>;
+#include <AnKi/Gr/BackendCommon/InstantiationMacros.def.h>
 
 #define ANKI_GR_OBJECT \
 	friend class GrManager; \

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

@@ -5,7 +5,6 @@
 
 #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 {
@@ -32,9 +31,7 @@ AccelerationStructureImpl::~AccelerationStructureImpl()
 {
 	if(m_handle)
 	{
-		ASGarbage* garbage = anki::newInstance<ASGarbage>(GrMemoryPool::getSingleton());
-		garbage->m_asHandle = m_handle;
-		VulkanFrameGarbageCollector::getSingleton().newASGarbage(garbage);
+		vkDestroyAccelerationStructureKHR(getVkDevice(), m_handle, nullptr);
 	}
 }
 

+ 5 - 5
AnKi/Gr/Vulkan/VkAccelerationStructure.h

@@ -59,19 +59,19 @@ private:
 	class ASBottomLevelInfo
 	{
 	public:
-		BufferPtr m_positionsBuffer;
-		BufferPtr m_indexBuffer;
+		BufferInternalPtr m_positionsBuffer;
+		BufferInternalPtr m_indexBuffer;
 	};
 
 	class ASTopLevelInfo
 	{
 	public:
-		BufferPtr m_instancesBuffer;
-		GrDynamicArray<AccelerationStructurePtr> m_blases;
+		BufferInternalPtr m_instancesBuffer;
+		GrDynamicArray<AccelerationStructureInternalPtr> m_blases;
 		U32 m_maxInstanceCount = 0; ///< Only for indirect.
 	};
 
-	BufferPtr m_asBuffer;
+	BufferInternalPtr m_asBuffer;
 	VkAccelerationStructureKHR m_handle = VK_NULL_HANDLE;
 	VkDeviceAddress m_deviceAddress = 0;
 

+ 15 - 19
AnKi/Gr/Vulkan/VkBuffer.cpp

@@ -4,7 +4,6 @@
 // http://www.anki3d.org/LICENSE
 
 #include <AnKi/Gr/Vulkan/VkBuffer.h>
-#include <AnKi/Gr/Vulkan/VkFrameGarbageCollector.h>
 #include <AnKi/Gr/Vulkan/VkGrManager.h>
 
 namespace anki {
@@ -92,24 +91,6 @@ BufferImpl::~BufferImpl()
 {
 	ANKI_ASSERT(!m_mapped);
 
-	BufferGarbage* garbage = anki::newInstance<BufferGarbage>(GrMemoryPool::getSingleton());
-	garbage->m_bufferHandle = m_handle;
-	garbage->m_memoryHandle = m_memHandle;
-
-	if(m_views.getSize())
-	{
-		garbage->m_viewHandles.resize(U32(m_views.getSize()));
-
-		U32 count = 0;
-		for(auto it : m_views)
-		{
-			const VkBufferView view = it;
-			garbage->m_viewHandles[count++] = view;
-		}
-	}
-
-	VulkanFrameGarbageCollector::getSingleton().newBufferGarbage(garbage);
-
 #if ANKI_ASSERTIONS_ENABLED
 	if(m_needsFlush && m_flushCount.load() == 0)
 	{
@@ -121,6 +102,21 @@ BufferImpl::~BufferImpl()
 		ANKI_VK_LOGW("Buffer needed invalidation but you never invalidated: %s", getName().cstr());
 	}
 #endif
+
+	for(VkBufferView view : m_views)
+	{
+		vkDestroyBufferView(getVkDevice(), view, nullptr);
+	}
+
+	if(m_handle)
+	{
+		vkDestroyBuffer(getVkDevice(), m_handle, nullptr);
+	}
+
+	if(m_memHandle)
+	{
+		GpuMemoryManager::getSingleton().freeMemory(m_memHandle);
+	}
 }
 
 Error BufferImpl::init(const BufferInitInfo& inf)

+ 3 - 19
AnKi/Gr/Vulkan/VkCommandBuffer.cpp

@@ -254,7 +254,6 @@ void CommandBuffer::bindSampler(U32 reg, U32 space, Sampler* sampler)
 
 	const VkSampler handle = static_cast<const SamplerImpl&>(*sampler).m_sampler->getHandle();
 	self.m_descriptorState.bindSampler(space, reg, handle);
-	self.m_microCmdb->pushObjectRef(sampler);
 }
 
 void CommandBuffer::bindConstantBuffer(U32 reg, U32 space, const BufferView& buff)
@@ -319,7 +318,6 @@ void CommandBuffer::bindSrv(U32 reg, U32 space, AccelerationStructure* as)
 
 	const VkAccelerationStructureKHR& handle = static_cast<const AccelerationStructureImpl&>(*as).getHandle();
 	self.m_descriptorState.bindAccelerationStructure(space, reg, &handle);
-	self.m_microCmdb->pushObjectRef(as);
 }
 
 void CommandBuffer::bindShaderProgram(ShaderProgram* prog)
@@ -365,8 +363,6 @@ void CommandBuffer::bindShaderProgram(ShaderProgram* prog)
 	}
 
 	self.m_descriptorState.setPipelineLayout(&impl.getPipelineLayout(), bindPoint);
-
-	self.m_microCmdb->pushObjectRef(prog);
 }
 
 void CommandBuffer::beginRenderPass(ConstWeakArray<RenderTarget> colorRts, RenderTarget* depthStencilRt, const TextureView& vrsRt, U8 vrsRtTexelSizeX,
@@ -836,8 +832,6 @@ void CommandBuffer::writeOcclusionQueriesResultToBuffer(ConstWeakArray<Occlusion
 
 		vkCmdCopyQueryPoolResults(self.m_handle, q->m_handle.getQueryPool(), q->m_handle.getQueryIndex(), 1, impl.getHandle(),
 								  buff.getOffset() * sizeof(U32) * i, sizeof(U32), VK_QUERY_RESULT_PARTIAL_BIT);
-
-		self.m_microCmdb->pushObjectRef(q);
 	}
 }
 
@@ -970,7 +964,7 @@ void CommandBuffer::setPipelineBarrier(ConstWeakArray<TextureBarrierInfo> textur
 	ANKI_VK_SELF(CommandBufferImpl);
 	self.commandCommon();
 
-	DynamicArray<VkImageMemoryBarrier, MemoryPoolPtrWrapper<StackMemoryPool>> imageBarriers(self.m_pool);
+	DynamicArray<VkImageMemoryBarrier, MemoryPoolPtrWrapper<StackMemoryPool>> imageBarriers(&self.m_pool);
 	VkMemoryBarrier genericBarrier = {};
 	genericBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
 	VkPipelineStageFlags srcStageMask = 0;
@@ -1003,8 +997,6 @@ void CommandBuffer::setPipelineBarrier(ConstWeakArray<TextureBarrierInfo> textur
 
 		genericBarrier.srcAccessMask |= memBarrier.srcAccessMask;
 		genericBarrier.dstAccessMask |= memBarrier.dstAccessMask;
-
-		self.m_microCmdb->pushObjectRef(barrier.m_as);
 	}
 
 	const Bool genericBarrierSet = genericBarrier.srcAccessMask != 0 && genericBarrier.dstAccessMask != 0;
@@ -1026,8 +1018,6 @@ void CommandBuffer::beginOcclusionQuery(OcclusionQuery* query)
 	ANKI_ASSERT(handle);
 
 	vkCmdBeginQuery(self.m_handle, handle, idx, 0);
-
-	self.m_microCmdb->pushObjectRef(query);
 }
 
 void CommandBuffer::endOcclusionQuery(OcclusionQuery* query)
@@ -1041,8 +1031,6 @@ void CommandBuffer::endOcclusionQuery(OcclusionQuery* query)
 	ANKI_ASSERT(handle);
 
 	vkCmdEndQuery(self.m_handle, handle, idx);
-
-	self.m_microCmdb->pushObjectRef(query);
 }
 
 void CommandBuffer::beginPipelineQuery(PipelineQuery* query)
@@ -1054,7 +1042,6 @@ void CommandBuffer::beginPipelineQuery(PipelineQuery* query)
 	const U32 idx = static_cast<const PipelineQueryImpl&>(*query).m_handle.getQueryIndex();
 	ANKI_ASSERT(handle);
 	vkCmdBeginQuery(self.m_handle, handle, idx, 0);
-	self.m_microCmdb->pushObjectRef(query);
 }
 
 void CommandBuffer::endPipelineQuery(PipelineQuery* query)
@@ -1066,7 +1053,6 @@ void CommandBuffer::endPipelineQuery(PipelineQuery* query)
 	const U32 idx = static_cast<const PipelineQueryImpl&>(*query).m_handle.getQueryIndex();
 	ANKI_ASSERT(handle);
 	vkCmdEndQuery(self.m_handle, handle, idx);
-	self.m_microCmdb->pushObjectRef(query);
 }
 
 void CommandBuffer::writeTimestamp(TimestampQuery* query)
@@ -1079,8 +1065,6 @@ void CommandBuffer::writeTimestamp(TimestampQuery* query)
 	const U32 idx = static_cast<const TimestampQueryImpl&>(*query).m_handle.getQueryIndex();
 
 	vkCmdWriteTimestamp(self.m_handle, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, handle, idx);
-
-	self.m_microCmdb->pushObjectRef(query);
 }
 
 Bool CommandBuffer::isEmpty() const
@@ -1178,9 +1162,9 @@ Error CommandBufferImpl::init(const CommandBufferInitInfo& init)
 	ANKI_CHECK(CommandBufferFactory::getSingleton().newCommandBuffer(m_tid, m_flags, m_microCmdb));
 	m_handle = m_microCmdb->getHandle();
 
-	m_pool = &m_microCmdb->getFastMemoryPool();
+	m_pool.init(GrMemoryPool::getSingleton().getAllocationCallback(), GrMemoryPool::getSingleton().getAllocationCallbackUserData(), 256_KB, 2.0f);
 
-	m_descriptorState.init(m_pool);
+	m_descriptorState.init(&m_pool);
 
 	m_debugMarkers = !!(getGrManagerImpl().getExtensions() & VulkanExtensions::kEXT_debug_utils);
 

+ 1 - 6
AnKi/Gr/Vulkan/VkCommandBuffer.h

@@ -44,11 +44,6 @@ public:
 
 	Error init(const CommandBufferInitInfo& init);
 
-	void setFence(VulkanMicroFence* fence)
-	{
-		m_microCmdb->setFence(fence);
-	}
-
 	const MicroCommandBufferPtr& getMicroCommandBuffer()
 	{
 		return m_microCmdb;
@@ -94,7 +89,7 @@ public:
 #endif
 
 private:
-	StackMemoryPool* m_pool = nullptr;
+	StackMemoryPool m_pool;
 
 	MicroCommandBufferPtr m_microCmdb;
 	VkCommandBuffer m_handle = VK_NULL_HANDLE;

+ 5 - 38
AnKi/Gr/Vulkan/VkCommandBufferFactory.cpp

@@ -12,16 +12,15 @@ namespace anki {
 
 static StatCounter g_commandBufferCountStatVar(StatCategory::kGr, "CommandBufferCount", StatFlag::kNone);
 
-void MicroCommandBufferPtrDeleter::operator()(MicroCommandBuffer* ptr)
+void MicroCommandBuffer::releaseInternal()
 {
-	ANKI_ASSERT(ptr);
-	ptr->m_threadAlloc->deleteCommandBuffer(ptr);
+	ANKI_TRACE_FUNCTION();
+	m_dsAllocator.reset();
+	m_threadAlloc->recycleCommandBuffer(this);
 }
 
 MicroCommandBuffer::~MicroCommandBuffer()
 {
-	reset();
-
 	m_dsAllocator.destroy();
 
 	if(m_handle)
@@ -36,23 +35,6 @@ MicroCommandBuffer::~MicroCommandBuffer()
 	}
 }
 
-void MicroCommandBuffer::reset()
-{
-	ANKI_TRACE_SCOPED_EVENT(VkCommandBufferReset);
-
-	ANKI_ASSERT(m_refcount.load() == 0);
-	ANKI_ASSERT(!m_fence.isCreated());
-
-	for(GrObjectType type : EnumIterable<GrObjectType>())
-	{
-		m_objectRefs[type].destroy();
-	}
-
-	m_dsAllocator.reset();
-
-	m_fastPool.reset();
-}
-
 Error CommandBufferThreadAllocator::init()
 {
 	ConstWeakArray<U32> families = getGrManagerImpl().getQueueFamilies();
@@ -134,34 +116,19 @@ Error CommandBufferThreadAllocator::newCommandBuffer(CommandBufferFlag cmdbFlags
 
 		MicroCommandBuffer* newCmdb = newInstance<MicroCommandBuffer>(GrMemoryPool::getSingleton(), this);
 
-		newCmdb->m_fastPool.init(GrMemoryPool::getSingleton().getAllocationCallback(), GrMemoryPool::getSingleton().getAllocationCallbackUserData(),
-								 256_KB, 2.0f);
-
-		for(DynamicArray<GrObjectPtr, MemoryPoolPtrWrapper<StackMemoryPool>>& arr : newCmdb->m_objectRefs)
-		{
-			arr = DynamicArray<GrObjectPtr, MemoryPoolPtrWrapper<StackMemoryPool>>(&newCmdb->m_fastPool);
-		}
-
 		newCmdb->m_handle = cmdb;
 		newCmdb->m_flags = cmdbFlags;
 		newCmdb->m_queue = queue;
 
 		out = newCmdb;
 	}
-	else
-	{
-		for([[maybe_unused]] GrObjectType type : EnumIterable<GrObjectType>())
-		{
-			ANKI_ASSERT(out->m_objectRefs[type].getSize() == 0);
-		}
-	}
 
 	ANKI_ASSERT(out && out->m_refcount.load() == 0);
 	outPtr.reset(out);
 	return Error::kNone;
 }
 
-void CommandBufferThreadAllocator::deleteCommandBuffer(MicroCommandBuffer* ptr)
+void CommandBufferThreadAllocator::recycleCommandBuffer(MicroCommandBuffer* ptr)
 {
 	ANKI_ASSERT(ptr);
 

+ 9 - 73
AnKi/Gr/Vulkan/VkCommandBufferFactory.h

@@ -19,20 +19,15 @@ class CommandBufferThreadAllocator;
 /// @addtogroup vulkan
 /// @{
 
-class MicroCommandBuffer : public IntrusiveListEnabled<MicroCommandBuffer>
+class MicroCommandBuffer
 {
 	friend class CommandBufferThreadAllocator;
-	friend class MicroCommandBufferPtrDeleter;
 
 public:
 	MicroCommandBuffer(CommandBufferThreadAllocator* allocator)
 		: m_threadAlloc(allocator)
 	{
 		ANKI_ASSERT(allocator);
-		for(DynamicArray<GrObjectPtr, MemoryPoolPtrWrapper<StackMemoryPool>>& arr : m_objectRefs)
-		{
-			arr = DynamicArray<GrObjectPtr, MemoryPoolPtrWrapper<StackMemoryPool>>(&m_fastPool);
-		}
 	}
 
 	~MicroCommandBuffer();
@@ -42,9 +37,12 @@ public:
 		m_refcount.fetchAdd(1);
 	}
 
-	I32 release() const
+	void release()
 	{
-		return m_refcount.fetchSub(1);
+		if(m_refcount.fetchSub(1) == 1)
+		{
+			releaseInternal();
+		}
 	}
 
 	I32 getRefcount() const
@@ -52,41 +50,12 @@ public:
 		return m_refcount.load();
 	}
 
-	void setFence(VulkanMicroFence* fence)
-	{
-		m_fence.reset(fence);
-	}
-
-	VulkanMicroFence* getFence() const
-	{
-		return m_fence.tryGet();
-	}
-
-	/// Interface method.
-	void onFenceDone()
-	{
-		reset();
-	}
-
-	StackMemoryPool& getFastMemoryPool()
-	{
-		return m_fastPool;
-	}
-
 	VkCommandBuffer getHandle() const
 	{
 		ANKI_ASSERT(m_handle);
 		return m_handle;
 	}
 
-	template<typename T>
-	void pushObjectRef(T* x)
-	{
-		ANKI_ASSERT(T::kClassType != GrObjectType::kTexture && T::kClassType != GrObjectType::kBuffer
-					&& "No need to push references of buffers and textures");
-		pushToArray(m_objectRefs[T::kClassType], x);
-	}
-
 	CommandBufferFlag getFlags() const
 	{
 		return m_flags;
@@ -104,14 +73,8 @@ public:
 	}
 
 private:
-	static constexpr U32 kMaxRefObjectSearch = 16;
-
-	StackMemoryPool m_fastPool;
 	VkCommandBuffer m_handle = {};
 
-	MicroFencePtr m_fence;
-	Array<DynamicArray<GrObjectPtr, MemoryPoolPtrWrapper<StackMemoryPool>>, U(GrObjectType::kCount)> m_objectRefs;
-
 	DescriptorAllocator m_dsAllocator;
 
 	CommandBufferThreadAllocator* m_threadAlloc;
@@ -119,38 +82,11 @@ private:
 	CommandBufferFlag m_flags = CommandBufferFlag::kNone;
 	GpuQueueType m_queue = GpuQueueType::kCount;
 
-	void reset();
-
-	void pushToArray(DynamicArray<GrObjectPtr, MemoryPoolPtrWrapper<StackMemoryPool>>& arr, GrObject* grobj)
-	{
-		ANKI_ASSERT(grobj);
-
-		// Search the temp cache to avoid setting the ref again
-		if(arr.getSize() >= kMaxRefObjectSearch)
-		{
-			for(U32 i = arr.getSize() - kMaxRefObjectSearch; i < arr.getSize(); ++i)
-			{
-				if(arr[i].get() == grobj)
-				{
-					return;
-				}
-			}
-		}
-
-		// Not found in the temp cache, add it
-		arr.emplaceBack(grobj);
-	}
-};
-
-/// Deleter.
-class MicroCommandBufferPtrDeleter
-{
-public:
-	void operator()(MicroCommandBuffer* buff);
+	void releaseInternal();
 };
 
 /// Micro command buffer pointer.
-using MicroCommandBufferPtr = IntrusivePtr<MicroCommandBuffer, MicroCommandBufferPtrDeleter>;
+using MicroCommandBufferPtr = IntrusiveNoDelPtr<MicroCommandBuffer>;
 
 /// Per-thread command buffer allocator.
 class alignas(ANKI_CACHE_LINE_SIZE) CommandBufferThreadAllocator
@@ -176,7 +112,7 @@ public:
 	Error newCommandBuffer(CommandBufferFlag cmdbFlags, MicroCommandBufferPtr& ptr);
 
 	/// It will recycle it.
-	void deleteCommandBuffer(MicroCommandBuffer* ptr);
+	void recycleCommandBuffer(MicroCommandBuffer* ptr);
 
 private:
 	ThreadId m_tid;

+ 10 - 0
AnKi/Gr/Vulkan/VkCommon.cpp

@@ -11,6 +11,16 @@
 
 namespace anki {
 
+void GrObjectDeleter::operator()(GrObject* ptr)
+{
+	getGrManagerImpl().releaseObject(ptr);
+}
+
+void GrObjectDeleterInternal::operator()(GrObject* ptr)
+{
+	getGrManagerImpl().releaseObjectDeleteLoop(ptr);
+}
+
 GrManagerImpl& getGrManagerImpl()
 {
 	return static_cast<GrManagerImpl&>(GrManager::getSingleton());

+ 11 - 0
AnKi/Gr/Vulkan/VkCommon.h

@@ -47,6 +47,17 @@ class GrManagerImpl;
 #define ANKI_VK_SELF(class_) class_& self = *static_cast<class_*>(this)
 #define ANKI_VK_SELF_CONST(class_) const class_& self = *static_cast<const class_*>(this)
 
+class GrObjectDeleterInternal
+{
+public:
+	void operator()(GrObject* ptr);
+};
+
+using GrObjectInternalPtr = IntrusivePtr<GrObject, GrObjectDeleterInternal>;
+
+#define ANKI_INSTANTIATE_GR_OBJECT(x) using x##InternalPtr = IntrusivePtr<x, GrObjectDeleterInternal>;
+#include <AnKi/Gr/BackendCommon/InstantiationMacros.def.h>
+
 ANKI_PURE GrManagerImpl& getGrManagerImpl();
 ANKI_PURE VkDevice getVkDevice();
 

+ 0 - 62
AnKi/Gr/Vulkan/VkFrameGarbageCollector.cpp

@@ -1,62 +0,0 @@
-// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
-// All rights reserved.
-// Code licensed under the BSD License.
-// http://www.anki3d.org/LICENSE
-
-#include <AnKi/Gr/Vulkan/VkFrameGarbageCollector.h>
-#include <AnKi/Gr/Vulkan/VkGrManager.h>
-#include <AnKi/Gr/Vulkan/VkDescriptor.h>
-
-namespace anki {
-
-TextureGarbage::~TextureGarbage()
-{
-	const VkDevice dev = getVkDevice();
-
-	for(VkImageView viewHandle : m_viewHandles)
-	{
-		vkDestroyImageView(dev, viewHandle, nullptr);
-	}
-
-	for(U32 bindlessIndex : m_bindlessIndices)
-	{
-		BindlessDescriptorSet::getSingleton().unbindTexture(bindlessIndex);
-	}
-
-	if(m_imageHandle)
-	{
-		vkDestroyImage(dev, m_imageHandle, nullptr);
-	}
-
-	if(m_memoryHandle)
-	{
-		GpuMemoryManager::getSingleton().freeMemory(m_memoryHandle);
-	}
-}
-
-BufferGarbage::~BufferGarbage()
-{
-	const VkDevice dev = getVkDevice();
-
-	for(VkBufferView view : m_viewHandles)
-	{
-		vkDestroyBufferView(dev, view, nullptr);
-	}
-
-	if(m_bufferHandle)
-	{
-		vkDestroyBuffer(dev, m_bufferHandle, nullptr);
-	}
-
-	if(m_memoryHandle)
-	{
-		GpuMemoryManager::getSingleton().freeMemory(m_memoryHandle);
-	}
-}
-
-ASGarbage::~ASGarbage()
-{
-	vkDestroyAccelerationStructureKHR(getGrManagerImpl().getDevice(), m_asHandle, nullptr);
-}
-
-} // end namespace anki

+ 0 - 57
AnKi/Gr/Vulkan/VkFrameGarbageCollector.h

@@ -1,57 +0,0 @@
-// Copyright (C) 2009-present, 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>
-#include <AnKi/Gr/Vulkan/VkGpuMemoryManager.h>
-#include <AnKi/Gr/BackendCommon/FrameGarbageCollector.h>
-
-namespace anki {
-
-/// @addtogroup vulkan
-/// @{
-
-/// @memberof FrameGarbageCollector
-class TextureGarbage : public IntrusiveListEnabled<TextureGarbage>
-{
-public:
-	GrDynamicArray<VkImageView> m_viewHandles;
-	GrDynamicArray<U32> m_bindlessIndices;
-	VkImage m_imageHandle = VK_NULL_HANDLE;
-	GpuMemoryHandle m_memoryHandle;
-
-	~TextureGarbage();
-};
-
-/// @memberof FrameGarbageCollector
-class BufferGarbage : public IntrusiveListEnabled<BufferGarbage>
-{
-public:
-	VkBuffer m_bufferHandle = VK_NULL_HANDLE;
-	GrDynamicArray<VkBufferView> m_viewHandles;
-	GpuMemoryHandle m_memoryHandle;
-
-	~BufferGarbage();
-};
-
-/// AS have more data (buffers) that build them but don't bother storing them since buffers will be automatically garbage collected as well.
-/// @memberof FrameGarbageCollector
-class ASGarbage : public IntrusiveListEnabled<ASGarbage>
-{
-public:
-	VkAccelerationStructureKHR m_asHandle = VK_NULL_HANDLE;
-
-	~ASGarbage();
-};
-
-class VulkanFrameGarbageCollector :
-	public FrameGarbageCollector<TextureGarbage, BufferGarbage, ASGarbage>,
-	public MakeSingleton<VulkanFrameGarbageCollector>
-{
-};
-/// @}
-
-} // end namespace anki

+ 57 - 65
AnKi/Gr/Vulkan/VkGrManager.cpp

@@ -3,6 +3,7 @@
 // Code licensed under the BSD License.
 // http://www.anki3d.org/LICENSE
 
+#include "AnKi/Util/Tracer.h"
 #include <AnKi/Gr/Vulkan/VkGrManager.h>
 #include <AnKi/Util/StringList.h>
 #include <AnKi/Core/App.h>
@@ -22,7 +23,6 @@
 #include <AnKi/Gr/Vulkan/VkFence.h>
 #include <AnKi/Gr/Vulkan/VkGpuMemoryManager.h>
 #include <AnKi/Gr/Vulkan/VkDescriptor.h>
-#include <AnKi/Gr/Vulkan/VkFrameGarbageCollector.h>
 
 #include <AnKi/Window/NativeWindow.h>
 #if ANKI_WINDOWING_SYSTEM_SDL
@@ -180,25 +180,22 @@ GrManagerImpl::~GrManagerImpl()
 	}
 
 	// 3rd THING: The destroy everything that has a reference to GrObjects.
-	CommandBufferFactory::freeSingleton();
+	m_crntSwapchain.reset(nullptr);
+	SwapchainFactory::freeSingleton();
 
-	for(PerFrame& frame : m_perFrame)
+	for(U32 frame = 0; frame < m_perFrame.getSize(); ++frame)
 	{
-		frame.m_acquireSemaphore.reset(nullptr);
-		frame.m_renderSemaphore.reset(nullptr);
+		m_frame = frame;
+		deleteObjectsMarkedForDeletion();
 	}
 
-	m_crntSwapchain.reset(nullptr);
-
 	// 4th THING: Continue with the rest
-
+	CommandBufferFactory::freeSingleton();
 	OcclusionQueryFactory::freeSingleton();
 	TimestampQueryFactory::freeSingleton();
 	PrimitivesPassedClippingFactory::freeSingleton();
 	SemaphoreFactory::freeSingleton(); // Destroy before fences
 	SamplerFactory::freeSingleton();
-	SwapchainFactory::freeSingleton(); // Destroy before fences
-	VulkanFrameGarbageCollector::freeSingleton();
 
 	GpuMemoryManager::freeSingleton();
 	PipelineLayoutFactory2::freeSingleton();
@@ -261,9 +258,6 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 	ANKI_CHECK(initSurface());
 	ANKI_CHECK(initDevice());
 
-	SwapchainFactory::allocateSingleton(U32(g_vsyncCVar));
-	m_crntSwapchain = SwapchainFactory::getSingleton().newInstance();
-
 	PipelineCache::allocateSingleton();
 	ANKI_CHECK(PipelineCache::getSingleton().init(init.m_cacheDirectory));
 
@@ -276,7 +270,9 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 	TimestampQueryFactory::allocateSingleton();
 	PrimitivesPassedClippingFactory::allocateSingleton();
 	SamplerFactory::allocateSingleton();
-	VulkanFrameGarbageCollector::allocateSingleton();
+
+	SwapchainFactory::allocateSingleton(U32(g_vsyncCVar));
+	m_crntSwapchain = SwapchainFactory::getSingleton().newInstance();
 
 	// See if unaligned formats are supported
 	{
@@ -1238,33 +1234,34 @@ void GrManagerImpl::beginFrameInternal()
 
 	LockGuard<Mutex> lock(m_globalMtx);
 
-	// Do that at begining frame, ALWAYS
+	// Do that at begin frame, ALWAYS
 	++m_frame;
-
-	// Wait for the oldest frame because we don't want to start the new one too early
 	PerFrame& frame = m_perFrame[m_frame % m_perFrame.getSize()];
+	ANKI_ASSERT(m_frameState == kFrameEnded);
+	m_frameState = kFrameStarted;
 
-	for(MicroFencePtr& fence : frame.m_fences)
+	// Wait for the oldest frame because we don't want to start the new one too early
 	{
-		if(fence)
+		ANKI_TRACE_SCOPED_EVENT(WaitFences);
+		for(MicroFencePtr& fence : frame.m_fences)
 		{
-			const Bool signaled = fence->clientWait(kMaxSecond);
-			if(!signaled)
+			if(fence)
 			{
-				ANKI_VK_LOGF("Timeout detected");
+				const Bool signaled = fence->clientWait(kMaxSecond);
+				ANKI_LOGI("Waited for %s", fence->getName().cstr());
+				if(!signaled)
+				{
+					ANKI_VK_LOGF("Timeout detected");
+				}
 			}
 		}
 	}
 
 	frame.m_fences.destroy();
-
-	// Reset the rest of the frame (after the fences)
-	frame.m_acquireSemaphore.reset(nullptr);
-	frame.m_renderSemaphore.reset(nullptr);
 	frame.m_queueWroteToSwapchainImage = GpuQueueType::kCount;
 
 	// Clear garbage
-	VulkanFrameGarbageCollector::getSingleton().beginFrameAndCollectGarbage();
+	deleteObjectsMarkedForDeletion();
 }
 
 TexturePtr GrManagerImpl::acquireNextPresentableTexture()
@@ -1272,24 +1269,23 @@ TexturePtr GrManagerImpl::acquireNextPresentableTexture()
 	ANKI_TRACE_FUNCTION();
 
 	// Create some objets outside the lock
-	MicroFencePtr fence = FenceFactory::getSingleton().newInstance("Acquire");
-	MicroSemaphorePtr acquireSemaphore = SemaphoreFactory::getSingleton().newInstance(fence, false, "Acquire");
+	Array<Char, 16> name;
+	snprintf(name.getBegin(), name.getSize(), "Acquire %lu", m_frame);
+	MicroFencePtr fence = FenceFactory::getSingleton().newInstance(name.getBegin());
 
 	LockGuard<Mutex> lock(m_globalMtx);
 
-	PerFrame& frame = m_perFrame[m_frame % m_perFrame.getSize()];
-
-	ANKI_ASSERT(!frame.m_acquireSemaphore.isCreated() && "Forgot to begin the frame");
+	ANKI_ASSERT(m_frameState == kFrameStarted);
+	m_frameState = kPresentableAcquired;
 
-	// Create sync objects
+	PerFrame& frame = m_perFrame[m_frame % m_perFrame.getSize()];
 	frame.m_fences.emplaceBack(fence);
-	frame.m_acquireSemaphore = acquireSemaphore;
 
 	// Get new image
+	const MicroSemaphore& acquireSemaphore = *m_crntSwapchain->m_acquireSemaphores[m_frame % m_crntSwapchain->m_acquireSemaphores.getSize()];
 	uint32_t imageIdx;
-
-	VkResult res = vkAcquireNextImageKHR(m_device, m_crntSwapchain->m_swapchain, UINT64_MAX, frame.m_acquireSemaphore->getHandle(),
-										 fence->getImplementation().m_handle, &imageIdx);
+	const VkResult res = vkAcquireNextImageKHR(m_device, m_crntSwapchain->m_swapchain, UINT64_MAX, acquireSemaphore.getHandle(),
+											   fence->getImplementation().m_handle, &imageIdx);
 
 	if(res == VK_ERROR_OUT_OF_DATE_KHR)
 	{
@@ -1303,9 +1299,10 @@ TexturePtr GrManagerImpl::acquireNextPresentableTexture()
 		}
 		m_crntSwapchain.reset(nullptr);
 		m_crntSwapchain = SwapchainFactory::getSingleton().newInstance();
+		const MicroSemaphore& acquireSemaphore = *m_crntSwapchain->m_acquireSemaphores[m_frame % m_crntSwapchain->m_acquireSemaphores.getSize()];
 
 		// Can't fail a second time
-		ANKI_VK_CHECKF(vkAcquireNextImageKHR(m_device, m_crntSwapchain->m_swapchain, UINT64_MAX, frame.m_acquireSemaphore->getHandle(),
+		ANKI_VK_CHECKF(vkAcquireNextImageKHR(m_device, m_crntSwapchain->m_swapchain, UINT64_MAX, acquireSemaphore.getHandle(),
 											 fence->getImplementation().m_handle, &imageIdx));
 	}
 	else
@@ -1314,7 +1311,7 @@ TexturePtr GrManagerImpl::acquireNextPresentableTexture()
 	}
 
 	m_acquiredImageIdx = U8(imageIdx);
-	return m_crntSwapchain->m_textures[imageIdx];
+	return TexturePtr(m_crntSwapchain->m_textures[imageIdx].get());
 }
 
 void GrManagerImpl::endFrameInternal()
@@ -1325,17 +1322,17 @@ void GrManagerImpl::endFrameInternal()
 
 	PerFrame& frame = m_perFrame[m_frame % m_perFrame.getSize()];
 
-	if(!frame.m_renderSemaphore)
-	{
-		ANKI_VK_LOGW("Nobody draw to the swapchain");
-	}
+	ANKI_ASSERT(m_frameState == kPresentableDrawn || m_frameState == kFrameStarted);
+	const Bool drawToPresentable = m_frameState == kPresentableDrawn;
+	m_frameState = kFrameEnded;
 
 	// Present
 	VkResult res;
 	VkPresentInfoKHR present = {};
 	present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
-	present.waitSemaphoreCount = (frame.m_renderSemaphore) ? 1 : 0;
-	present.pWaitSemaphores = (frame.m_renderSemaphore) ? &frame.m_renderSemaphore->getHandle() : nullptr;
+	present.waitSemaphoreCount = (drawToPresentable) ? 1 : 0;
+	present.pWaitSemaphores =
+		(drawToPresentable) ? &m_crntSwapchain->m_renderSemaphores[m_frame % m_crntSwapchain->m_renderSemaphores.getSize()]->getHandle() : nullptr;
 	present.swapchainCount = 1;
 	present.pSwapchains = &m_crntSwapchain->m_swapchain;
 	const U32 idx = m_acquiredImageIdx;
@@ -1385,8 +1382,7 @@ void GrManagerImpl::submitInternal(WeakArray<CommandBuffer*> cmdbs, WeakArray<Fe
 		cmdb.setSubmitted();
 #endif
 
-		MicroCommandBuffer& mcmdb = *cmdb.getMicroCommandBuffer();
-		mcmdb.setFence(fence.get());
+		const MicroCommandBuffer& mcmdb = *cmdb.getMicroCommandBuffer();
 
 		if(i == 0)
 		{
@@ -1416,9 +1412,6 @@ void GrManagerImpl::submitInternal(WeakArray<CommandBuffer*> cmdbs, WeakArray<Fe
 		waitSemaphores.emplaceBack(msem.getHandle());
 		waitStages.emplaceBack(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
 		waitTimelineValues.emplaceBack(msem.getSemaphoreValue());
-
-		// Refresh the fence because the semaphore can't be recycled until the current submission is done
-		msem.setFence(fence.get());
 	}
 
 	// Signal semaphore
@@ -1427,7 +1420,7 @@ void GrManagerImpl::submitInternal(WeakArray<CommandBuffer*> cmdbs, WeakArray<Fe
 	if(signalFence)
 	{
 		FenceImpl* fenceImpl = anki::newInstance<FenceImpl>(GrMemoryPool::getSingleton(), "SignalFence");
-		fenceImpl->m_semaphore = SemaphoreFactory::getSingleton().newInstance(fence, true, "SubmitSignal");
+		fenceImpl->m_semaphore = SemaphoreFactory::getSingleton().newInstance(true, "SubmitSignal");
 
 		signalFence->reset(fenceImpl);
 
@@ -1442,10 +1435,14 @@ void GrManagerImpl::submitInternal(WeakArray<CommandBuffer*> cmdbs, WeakArray<Fe
 
 		// Do some special stuff for the last command buffer
 		GrManagerImpl::PerFrame& frame = m_perFrame[m_frame % m_perFrame.getSize()];
+		const MicroSemaphore& acquireSemaphore = *m_crntSwapchain->m_acquireSemaphores[m_frame % m_crntSwapchain->m_acquireSemaphores.getSize()];
 		if(renderedToDefaultFb)
 		{
+			ANKI_ASSERT(m_frameState == kPresentableAcquired);
+			m_frameState = kPresentableDrawn;
+
 			// Wait semaphore
-			waitSemaphores.emplaceBack(frame.m_acquireSemaphore->getHandle());
+			waitSemaphores.emplaceBack(acquireSemaphore.getHandle());
 
 			// That depends on how we use the swapchain img. Be a bit conservative
 			waitStages.emplaceBack(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
@@ -1453,24 +1450,19 @@ void GrManagerImpl::submitInternal(WeakArray<CommandBuffer*> cmdbs, WeakArray<Fe
 			// Set something
 			waitTimelineValues.emplaceBack(0);
 
-			// Refresh the fence because the semaphore can't be recycled until the current submission is done
-			frame.m_acquireSemaphore->setFence(fence.get());
-
-			// Create the semaphore to signal and then wait on present
-			ANKI_ASSERT(!frame.m_renderSemaphore && "Only one begin/end render pass is allowed with the default fb");
-			frame.m_renderSemaphore = SemaphoreFactory::getSingleton().newInstance(fence, false, "RenderToSwapchain");
-			frame.m_renderSemaphore->setFence(fence.get());
-
-			signalSemaphores.emplaceBack(frame.m_renderSemaphore->getHandle());
+			// Get the semaphore to signal and then wait on present
+			const MicroSemaphore& renderSemaphore = *m_crntSwapchain->m_renderSemaphores[m_frame & m_crntSwapchain->m_renderSemaphores.getSize()];
+			signalSemaphores.emplaceBack(renderSemaphore.getHandle());
 
 			// Increment the timeline values as well because the spec wants a dummy value even for non-timeline semaphores
 			signalTimelineValues.emplaceBack(0);
 
-			// Update the swapchain's fence
-			m_crntSwapchain->setFence(fence.get());
-
 			frame.m_queueWroteToSwapchainImage = queueType;
 		}
+		else
+		{
+			ANKI_ASSERT(m_frameState == kFrameStarted);
+		}
 
 		frame.m_fences.emplaceBack(fence);
 
@@ -1689,7 +1681,7 @@ VkBool32 GrManagerImpl::debugReportCallbackEXT(VkDebugUtilsMessageSeverityFlagBi
 
 	if(messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
 	{
-		ANKI_VK_LOGE("VK debug report: %s. Affected objects: %s", pCallbackData->pMessage, objectNames.cstr());
+		ANKI_VK_LOGI("VK debug report: %s. Affected objects: %s", pCallbackData->pMessage, objectNames.cstr()); // TODO
 	}
 	else if(messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
 	{

+ 49 - 15
AnKi/Gr/Vulkan/VkGrManager.h

@@ -5,6 +5,7 @@
 
 #pragma once
 
+#include "AnKi/Util/Tracer.h"
 #include <AnKi/Gr/GrManager.h>
 #include <AnKi/Gr/Vulkan/VkCommon.h>
 #include <AnKi/Gr/Vulkan/VkSemaphoreFactory.h>
@@ -146,7 +147,39 @@ public:
 	/// @note It's thread-safe.
 	void printPipelineShaderInfo(VkPipeline ppline, CString name, U64 hash = 0) const;
 
+	/// @note It's thread-safe.
+	void releaseObject(GrObject* object)
+	{
+		ANKI_ASSERT(object);
+		LockGuard lock(m_globalMtx);
+		m_perFrame[m_frame % m_perFrame.getSize()].m_objectsMarkedForDeletion.emplaceBack(object);
+	}
+
+	void releaseObjectDeleteLoop(GrObject* object)
+	{
+		ANKI_ASSERT(object);
+		m_perFrame[m_frame % m_perFrame.getSize()].m_objectsMarkedForDeletion.emplaceBack(object);
+	}
+
 private:
+	enum FrameState : U8
+	{
+		kFrameStarted,
+		kPresentableAcquired,
+		kPresentableDrawn,
+		kFrameEnded,
+	};
+
+	class PerFrame
+	{
+	public:
+		GrDynamicArray<MicroFencePtr> m_fences;
+
+		GpuQueueType m_queueWroteToSwapchainImage = GpuQueueType::kCount;
+
+		GrDynamicArray<GrObject*> m_objectsMarkedForDeletion;
+	};
+
 	U64 m_frame = 0;
 
 #if ANKI_GR_MANAGER_DEBUG_MEMMORY
@@ -176,28 +209,14 @@ private:
 
 	mutable SpinLock m_shaderStatsMtx;
 
-	/// @name Surface_related
-	/// @{
-	class PerFrame
-	{
-	public:
-		GrDynamicArray<MicroFencePtr> m_fences;
-		MicroSemaphorePtr m_acquireSemaphore;
-
-		/// Signaled by the submit that renders to the default FB. Present waits for it.
-		MicroSemaphorePtr m_renderSemaphore;
-
-		GpuQueueType m_queueWroteToSwapchainImage = GpuQueueType::kCount;
-	};
-
 	VkSurfaceKHR m_surface = VK_NULL_HANDLE;
 	U32 m_nativeWindowWidth = 0;
 	U32 m_nativeWindowHeight = 0;
 	MicroSwapchainPtr m_crntSwapchain;
 	U8 m_acquiredImageIdx = kMaxU8;
+	FrameState m_frameState = kFrameEnded;
 
 	Array<PerFrame, kMaxFramesInFlight> m_perFrame;
-	/// @}
 
 	VkPhysicalDeviceMemoryProperties m_memoryProperties;
 
@@ -237,6 +256,21 @@ private:
 		features2.pNext = &features;
 		vkGetPhysicalDeviceFeatures2(m_physicalDevice, &features2);
 	}
+
+	void deleteObjectsMarkedForDeletion()
+	{
+		ANKI_TRACE_FUNCTION();
+		PerFrame& frame = m_perFrame[m_frame % m_perFrame.getSize()];
+		while(!frame.m_objectsMarkedForDeletion.isEmpty())
+		{
+			GrDynamicArray<GrObject*> objects = std::move(frame.m_objectsMarkedForDeletion);
+
+			for(GrObject* obj : objects)
+			{
+				deleteInstance(GrMemoryPool::getSingleton(), obj);
+			}
+		}
+	}
 };
 /// @}
 

+ 16 - 23
AnKi/Gr/Vulkan/VkSemaphoreFactory.cpp

@@ -9,12 +9,9 @@
 
 namespace anki {
 
-MicroSemaphore::MicroSemaphore(MicroFencePtr fence, Bool isTimeline)
-	: m_fence(fence)
-	, m_isTimeline(isTimeline)
+MicroSemaphore::MicroSemaphore(Bool isTimeline)
+	: m_isTimeline(isTimeline)
 {
-	ANKI_ASSERT(fence.isCreated());
-
 	VkSemaphoreTypeCreateInfo typeCreateInfo = {};
 	typeCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO;
 	typeCreateInfo.semaphoreType = (m_isTimeline) ? VK_SEMAPHORE_TYPE_TIMELINE : VK_SEMAPHORE_TYPE_BINARY;
@@ -36,6 +33,18 @@ MicroSemaphore::~MicroSemaphore()
 	}
 }
 
+void MicroSemaphore::releaseInternal()
+{
+	if(m_isTimeline)
+	{
+		SemaphoreFactory::getSingleton().m_timelineRecycler.recycle(this);
+	}
+	else
+	{
+		SemaphoreFactory::getSingleton().m_binaryRecycler.recycle(this);
+	}
+}
+
 Bool MicroSemaphore::clientWait(Second seconds)
 {
 	ANKI_ASSERT(m_isTimeline);
@@ -58,33 +67,17 @@ Bool MicroSemaphore::clientWait(Second seconds)
 	return res != VK_TIMEOUT;
 }
 
-void MicroSemaphorePtrDeleter::operator()(MicroSemaphore* s)
+MicroSemaphorePtr SemaphoreFactory::newInstance(Bool isTimeline, CString name)
 {
-	ANKI_ASSERT(s);
-	if(s->m_isTimeline)
-	{
-		SemaphoreFactory::getSingleton().m_timelineRecycler.recycle(s);
-	}
-	else
-	{
-		SemaphoreFactory::getSingleton().m_binaryRecycler.recycle(s);
-	}
-}
-
-MicroSemaphorePtr SemaphoreFactory::newInstance(MicroFencePtr fence, Bool isTimeline, CString name)
-{
-	ANKI_ASSERT(fence);
-
 	MicroSemaphore* out = (isTimeline) ? m_timelineRecycler.findToReuse() : m_binaryRecycler.findToReuse();
 
 	if(out == nullptr)
 	{
 		// Create a new one
-		out = anki::newInstance<MicroSemaphore>(GrMemoryPool::getSingleton(), fence, isTimeline);
+		out = anki::newInstance<MicroSemaphore>(GrMemoryPool::getSingleton(), isTimeline);
 	}
 	else
 	{
-		out->m_fence = fence;
 		ANKI_ASSERT(out->m_isTimeline == isTimeline);
 		if(out->m_isTimeline)
 		{

+ 10 - 31
AnKi/Gr/Vulkan/VkSemaphoreFactory.h

@@ -5,7 +5,7 @@
 
 #pragma once
 
-#include <AnKi/Gr/Vulkan/VkFenceFactory.h>
+#include <AnKi/Gr/Vulkan/VkCommon.h>
 #include <AnKi/Gr/BackendCommon/MicroObjectRecycler.h>
 
 namespace anki {
@@ -36,9 +36,12 @@ public:
 		m_refcount.fetchAdd(1);
 	}
 
-	I32 release() const
+	void release()
 	{
-		return m_refcount.fetchSub(1);
+		if(m_refcount.fetchSub(1) == 1)
+		{
+			releaseInternal();
+		}
 	}
 
 	I32 getRefcount() const
@@ -46,22 +49,6 @@ public:
 		return m_refcount.load();
 	}
 
-	VulkanMicroFence* getFence() const
-	{
-		return m_fence.tryGet();
-	}
-
-	/// Interface method.
-	void onFenceDone()
-	{
-		// Do nothing
-	}
-
-	void setFence(VulkanMicroFence* fence)
-	{
-		m_fence.reset(fence);
-	}
-
 	Bool clientWait(Second seconds);
 
 	Bool isTimeline() const
@@ -89,26 +76,18 @@ private:
 	VkSemaphore m_handle = VK_NULL_HANDLE;
 	mutable Atomic<I32> m_refcount = {0};
 
-	/// Fence to find out when it's safe to reuse this semaphore.
-	MicroFencePtr m_fence;
-
 	Atomic<U64> m_timelineValue = {0};
 	Bool m_isTimeline = false;
 
-	MicroSemaphore(MicroFencePtr fence, Bool isTimeline);
+	MicroSemaphore(Bool isTimeline);
 
 	~MicroSemaphore();
-};
 
-/// MicroSemaphorePtr deleter.
-class MicroSemaphorePtrDeleter
-{
-public:
-	void operator()(MicroSemaphore* s);
+	void releaseInternal();
 };
 
 /// MicroSemaphore smart pointer.
-using MicroSemaphorePtr = IntrusivePtr<MicroSemaphore, MicroSemaphorePtrDeleter>;
+using MicroSemaphorePtr = IntrusiveNoDelPtr<MicroSemaphore>;
 
 /// Factory of semaphores.
 class SemaphoreFactory : public MakeSingleton<SemaphoreFactory>
@@ -123,7 +102,7 @@ public:
 		m_timelineRecycler.destroy();
 	}
 
-	MicroSemaphorePtr newInstance(MicroFencePtr fence, Bool isTimeline, CString name);
+	MicroSemaphorePtr newInstance(Bool isTimeline, CString name);
 
 private:
 	MicroObjectRecycler<MicroSemaphore> m_binaryRecycler;

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

@@ -223,7 +223,7 @@ Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 	// Link reflection
 	//
 	Bool firstLink = true;
-	for(ShaderPtr& shader : m_shaders)
+	for(ShaderInternalPtr& shader : m_shaders)
 	{
 		m_shaderTypes |= ShaderTypeBit(1 << shader->getShaderType());
 
@@ -430,7 +430,7 @@ Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 
 	// Get shader sizes and a few other things
 	//
-	for(const ShaderPtr& s : m_shaders)
+	for(const ShaderInternalPtr& s : m_shaders)
 	{
 		if(!s.isCreated())
 		{

+ 2 - 2
AnKi/Gr/Vulkan/VkShaderProgram.h

@@ -90,7 +90,7 @@ public:
 	}
 
 private:
-	GrDynamicArray<ShaderPtr> m_shaders;
+	GrDynamicArray<ShaderInternalPtr> m_shaders;
 
 	PipelineLayout2* m_pplineLayout = nullptr;
 
@@ -114,7 +114,7 @@ private:
 		VkPipeline m_ppline = VK_NULL_HANDLE;
 		GrDynamicArray<U8> m_allHandles;
 		U32 m_missShaderCount = 0;
-		BufferPtr m_allHandlesBuff;
+		BufferInternalPtr m_allHandlesBuff;
 	} m_rt;
 
 	void rewriteSpirv(ShaderReflectionDescriptorRelated& refl, GrDynamicArray<GrDynamicArray<U32>>& rewrittenSpirvs);

+ 11 - 1
AnKi/Gr/Vulkan/VkSwapchainFactory.cpp

@@ -211,6 +211,8 @@ Error MicroSwapchain::initInternal()
 		}
 
 		m_textures.resize(count);
+		m_acquireSemaphores.resize(count);
+		m_renderSemaphores.resize(count);
 
 		ANKI_VK_LOGI("Created a swapchain. Image count: %u, present mode: %u, size: %ux%u, vsync: %u", count, presentMode, surfaceWidth,
 					 surfaceHeight, U32(SwapchainFactory::getSingleton().m_vsync));
@@ -220,7 +222,7 @@ Error MicroSwapchain::initInternal()
 		ANKI_VK_CHECK(vkGetSwapchainImagesKHR(dev, m_swapchain, &count, &images[0]));
 		for(U32 i = 0; i < count; ++i)
 		{
-			TextureInitInfo init("SwapchainImg");
+			TextureInitInfo init(GrString().sprintf("SwapchainImage #%u", i));
 			init.m_width = surfaceWidth;
 			init.m_height = surfaceHeight;
 			init.m_format = Format(surfaceFormat); // anki::Format is compatible with VkFormat
@@ -231,12 +233,20 @@ Error MicroSwapchain::initInternal()
 			TextureImpl* tex = newInstance<TextureImpl>(GrMemoryPool::getSingleton(), init.getName());
 			m_textures[i].reset(tex);
 			ANKI_CHECK(tex->initExternal(images[i], init));
+
+			m_acquireSemaphores[i] = SemaphoreFactory::getSingleton().newInstance(false, GrString().sprintf("Acquire #%u", i));
+			m_renderSemaphores[i] = SemaphoreFactory::getSingleton().newInstance(false, GrString().sprintf("Present #%u", i));
 		}
 	}
 
 	return Error::kNone;
 }
 
+void MicroSwapchain::releaseInternal()
+{
+	SwapchainFactory::getSingleton().m_recycler.recycle(this);
+}
+
 MicroSwapchainPtr SwapchainFactory::newInstance()
 {
 	// Delete stale swapchains (they are stale because they are probably out of data) and always create a new one

+ 12 - 35
AnKi/Gr/Vulkan/VkSwapchainFactory.h

@@ -5,8 +5,9 @@
 
 #pragma once
 
-#include <AnKi/Gr/Vulkan/VkFenceFactory.h>
+#include <AnKi/Gr/Vulkan/VkCommon.h>
 #include <AnKi/Gr/BackendCommon/MicroObjectRecycler.h>
+#include <AnKi/Gr/Vulkan/VkSemaphoreFactory.h>
 #include <AnKi/Util/Ptr.h>
 
 namespace anki {
@@ -23,7 +24,9 @@ class MicroSwapchain
 public:
 	VkSwapchainKHR m_swapchain = {};
 
-	GrDynamicArray<TexturePtr> m_textures;
+	GrDynamicArray<TextureInternalPtr> m_textures;
+	GrDynamicArray<MicroSemaphorePtr> m_acquireSemaphores;
+	GrDynamicArray<MicroSemaphorePtr> m_renderSemaphores; ///< Signaled by the operation that renders to a presentable image.
 
 	MicroSwapchain();
 
@@ -34,9 +37,12 @@ public:
 		m_refcount.fetchAdd(1);
 	}
 
-	I32 release() const
+	void release()
 	{
-		return m_refcount.fetchSub(1);
+		if(m_refcount.fetchSub(1) == 1)
+		{
+			releaseInternal();
+		}
 	}
 
 	I32 getRefcount() const
@@ -44,43 +50,20 @@ public:
 		return m_refcount.load();
 	}
 
-	void setFence(VulkanMicroFence* fence)
-	{
-		m_fence.reset(fence);
-	}
-
-	VulkanMicroFence* getFence() const
-	{
-		return m_fence.tryGet();
-	}
-
-	/// Interface method.
-	void onFenceDone()
-	{
-		// Do nothing
-	}
-
 private:
-	MicroFencePtr m_fence;
 	mutable Atomic<I32> m_refcount = {0};
 
 	Error initInternal();
-};
 
-/// Deleter for MicroSwapchainPtr smart pointer.
-class MicroSwapchainPtrDeleter
-{
-public:
-	void operator()(MicroSwapchain* x);
+	void releaseInternal();
 };
 
 /// MicroSwapchain smart pointer.
-using MicroSwapchainPtr = IntrusivePtr<MicroSwapchain, MicroSwapchainPtrDeleter>;
+using MicroSwapchainPtr = IntrusiveNoDelPtr<MicroSwapchain>;
 
 /// Swapchain factory.
 class SwapchainFactory : public MakeSingleton<SwapchainFactory>
 {
-	friend class MicroSwapchainPtrDeleter;
 	friend class MicroSwapchain;
 
 public:
@@ -102,10 +85,4 @@ private:
 };
 /// @}
 
-inline void MicroSwapchainPtrDeleter::operator()(MicroSwapchain* s)
-{
-	ANKI_ASSERT(s);
-	SwapchainFactory::getSingleton().m_recycler.recycle(s);
-}
-
 } // end namespace anki

+ 9 - 11
AnKi/Gr/Vulkan/VkTexture.cpp

@@ -6,7 +6,6 @@
 #include <AnKi/Gr/Vulkan/VkTexture.h>
 #include <AnKi/Gr/Vulkan/VkGrManager.h>
 #include <AnKi/Gr/Vulkan/VkDescriptor.h>
-#include <AnKi/Gr/Vulkan/VkFrameGarbageCollector.h>
 
 namespace anki {
 
@@ -78,17 +77,15 @@ TextureImpl::~TextureImpl()
 	}
 #endif
 
-	TextureGarbage* garbage = anki::newInstance<TextureGarbage>(GrMemoryPool::getSingleton());
-
 	auto destroyTextureView = [&](TextureViewEntry& entry) {
-		if(entry.m_handle)
+		if(entry.m_bindlessIndex != kMaxU32)
 		{
-			garbage->m_viewHandles.emplaceBack(entry.m_handle);
+			BindlessDescriptorSet::getSingleton().unbindTexture(entry.m_bindlessIndex);
 		}
 
-		if(entry.m_bindlessIndex != kMaxU32)
+		if(entry.m_handle)
 		{
-			garbage->m_bindlessIndices.emplaceBack(entry.m_bindlessIndex);
+			vkDestroyImageView(getVkDevice(), entry.m_handle, nullptr);
 		}
 	};
 
@@ -104,12 +101,13 @@ TextureImpl::~TextureImpl()
 
 	if(m_imageHandle && !(m_usage & TextureUsageBit::kPresent))
 	{
-		garbage->m_imageHandle = m_imageHandle;
+		vkDestroyImage(getVkDevice(), m_imageHandle, nullptr);
 	}
 
-	garbage->m_memoryHandle = m_memHandle;
-
-	VulkanFrameGarbageCollector::getSingleton().newTextureGarbage(garbage);
+	if(m_memHandle)
+	{
+		GpuMemoryManager::getSingleton().freeMemory(m_memHandle);
+	}
 }
 
 Error TextureImpl::initInternal(VkImage externalImage, const TextureInitInfo& init_)

+ 1 - 1
Samples/Common/SampleApp.h

@@ -14,7 +14,7 @@ class SampleApp : public App
 public:
 	using App::App;
 
-	Error init(int argc, char** argv, CString sampleName);
+	Error userInit(int argc, char** argv, CString sampleName);
 	Error userMainLoop(Bool& quit, Second elapsedTime) override;
 
 	virtual Error sampleExtraInit() = 0;

+ 1 - 1
Samples/Sponza/Main.cpp

@@ -13,7 +13,7 @@ class MyApp : public SampleApp
 public:
 	using SampleApp::SampleApp;
 
-	Error sampleExtraInit()
+	Error sampleExtraInit() final
 	{
 		ScriptResourcePtr script;
 		ANKI_CHECK(ResourceManager::getSingleton().loadResource("Assets/Scene.lua", script));