瀏覽代碼

Make resource allocations thread-safe

Panagiotis Christopoulos Charitos 9 月之前
父節點
當前提交
8ed2cfbdbd

+ 4 - 1
AnKi/Resource/AnimationResource.h

@@ -58,7 +58,10 @@ public:
 class AnimationResource : public ResourceObject
 {
 public:
-	AnimationResource() = default;
+	AnimationResource(CString fname, U32 uuid)
+		: ResourceObject(fname, uuid)
+	{
+	}
 
 	~AnimationResource() = default;
 

+ 3 - 11
AnKi/Resource/Common.cpp

@@ -8,21 +8,13 @@
 
 namespace anki {
 
-// Instan
-
 template<typename T>
 void ResourcePtrDeleter<T>::operator()(T* ptr)
 {
-	ResourceManager::getSingleton().unregisterResource(ptr);
-	deleteInstance(ResourceMemoryPool::getSingleton(), ptr);
+	ResourceManager::getSingleton().freeResource(ptr);
 }
 
-#define ANKI_INSTANTIATE_RESOURCE(rsrc_, ptr_) template void ResourcePtrDeleter<rsrc_>::operator()(rsrc_* ptr);
-
-#define ANKI_INSTANSIATE_RESOURCE_DELIMITER()
-
-#include <AnKi/Resource/InstantiationMacros.h>
-#undef ANKI_INSTANTIATE_RESOURCE
-#undef ANKI_INSTANSIATE_RESOURCE_DELIMITER
+#define ANKI_INSTANTIATE_RESOURCE(className) template void ResourcePtrDeleter<className>::operator()(className* ptr);
+#include <AnKi/Resource/Resources.def.h>
 
 } // end namespace anki

+ 4 - 9
AnKi/Resource/Common.h

@@ -57,15 +57,10 @@ template<typename T>
 using ResourcePtr = IntrusivePtr<T, ResourcePtrDeleter<T>>;
 
 // NOTE: Add resources in 3 places
-#define ANKI_INSTANTIATE_RESOURCE(rsrc_, name_) \
-	class rsrc_; \
-	using name_ = ResourcePtr<rsrc_>;
-
-#define ANKI_INSTANSIATE_RESOURCE_DELIMITER()
-
-#include <AnKi/Resource/InstantiationMacros.h>
-#undef ANKI_INSTANTIATE_RESOURCE
-#undef ANKI_INSTANSIATE_RESOURCE_DELIMITER
+#define ANKI_INSTANTIATE_RESOURCE(className) \
+	class className; \
+	using className##Ptr = ResourcePtr<className>;
+#include <AnKi/Resource/Resources.def.h>
 
 /// An alias that denotes a ResourceFilesystem path.
 using ResourceFilename = CString;

+ 4 - 1
AnKi/Resource/CpuMeshResource.h

@@ -20,7 +20,10 @@ class CpuMeshResource : public ResourceObject
 {
 public:
 	/// Default constructor
-	CpuMeshResource() = default;
+	CpuMeshResource(CString fname, U32 uuid)
+		: ResourceObject(fname, uuid)
+	{
+	}
 
 	~CpuMeshResource() = default;
 

+ 4 - 1
AnKi/Resource/DummyResource.h

@@ -17,7 +17,10 @@ namespace anki {
 class DummyResource : public ResourceObject
 {
 public:
-	DummyResource() = default;
+	DummyResource(CString fname, U32 uuid)
+		: ResourceObject(fname, uuid)
+	{
+	}
 
 	~DummyResource()
 	{

+ 4 - 1
AnKi/Resource/GenericResource.h

@@ -17,7 +17,10 @@ namespace anki {
 class GenericResource : public ResourceObject
 {
 public:
-	GenericResource() = default;
+	GenericResource(CString fname, U32 uuid)
+		: ResourceObject(fname, uuid)
+	{
+	}
 
 	~GenericResource() = default;
 

+ 4 - 1
AnKi/Resource/ImageAtlasResource.h

@@ -34,7 +34,10 @@ namespace anki {
 class ImageAtlasResource : public ResourceObject
 {
 public:
-	ImageAtlasResource() = default;
+	ImageAtlasResource(CString fname, U32 uuid)
+		: ResourceObject(fname, uuid)
+	{
+	}
 
 	~ImageAtlasResource() = default;
 

+ 4 - 1
AnKi/Resource/ImageResource.h

@@ -20,7 +20,10 @@ inline NumericCVar<U32> g_maxImageSizeCVar("Rsrc", "MaxImageSize", 1024u * 1024u
 class ImageResource : public ResourceObject
 {
 public:
-	ImageResource() = default;
+	ImageResource(CString fname, U32 uuid)
+		: ResourceObject(fname, uuid)
+	{
+	}
 
 	~ImageResource();
 

+ 0 - 32
AnKi/Resource/InstantiationMacros.h

@@ -1,32 +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
-
-// An awful trick to instantiate stuff with the resource type
-
-ANKI_INSTANTIATE_RESOURCE(AnimationResource, AnimationResourcePtr)
-ANKI_INSTANSIATE_RESOURCE_DELIMITER()
-ANKI_INSTANTIATE_RESOURCE(ImageResource, ImageResourcePtr)
-ANKI_INSTANSIATE_RESOURCE_DELIMITER()
-ANKI_INSTANTIATE_RESOURCE(MeshResource, MeshResourcePtr)
-ANKI_INSTANSIATE_RESOURCE_DELIMITER()
-ANKI_INSTANTIATE_RESOURCE(CpuMeshResource, CpuMeshResourcePtr)
-ANKI_INSTANSIATE_RESOURCE_DELIMITER()
-ANKI_INSTANTIATE_RESOURCE(SkeletonResource, SkeletonResourcePtr)
-ANKI_INSTANSIATE_RESOURCE_DELIMITER()
-ANKI_INSTANTIATE_RESOURCE(ParticleEmitterResource, ParticleEmitterResourcePtr)
-ANKI_INSTANSIATE_RESOURCE_DELIMITER()
-ANKI_INSTANTIATE_RESOURCE(ModelResource, ModelResourcePtr)
-ANKI_INSTANSIATE_RESOURCE_DELIMITER()
-ANKI_INSTANTIATE_RESOURCE(ScriptResource, ScriptResourcePtr)
-ANKI_INSTANSIATE_RESOURCE_DELIMITER()
-ANKI_INSTANTIATE_RESOURCE(DummyResource, DummyResourcePtr)
-ANKI_INSTANSIATE_RESOURCE_DELIMITER()
-ANKI_INSTANTIATE_RESOURCE(GenericResource, GenericResourcePtr)
-ANKI_INSTANSIATE_RESOURCE_DELIMITER()
-ANKI_INSTANTIATE_RESOURCE(ImageAtlasResource, ImageAtlasResourcePtr)
-ANKI_INSTANSIATE_RESOURCE_DELIMITER()
-ANKI_INSTANTIATE_RESOURCE(ShaderProgramResource, ShaderProgramResourcePtr)
-ANKI_INSTANSIATE_RESOURCE_DELIMITER()
-ANKI_INSTANTIATE_RESOURCE(MaterialResource, MaterialResourcePtr)

+ 0 - 4
AnKi/Resource/MaterialResource.cpp

@@ -77,10 +77,6 @@ MaterialVariable::~MaterialVariable()
 {
 }
 
-MaterialResource::MaterialResource()
-{
-}
-
 MaterialResource::~MaterialResource()
 {
 	ResourceMemoryPool::getSingleton().free(m_prefilledLocalConstants);

+ 4 - 1
AnKi/Resource/MaterialResource.h

@@ -182,7 +182,10 @@ private:
 class MaterialResource : public ResourceObject
 {
 public:
-	MaterialResource();
+	MaterialResource(CString fname, U32 uuid)
+		: ResourceObject(fname, uuid)
+	{
+	}
 
 	~MaterialResource();
 

+ 0 - 4
AnKi/Resource/MeshResource.cpp

@@ -49,10 +49,6 @@ public:
 	}
 };
 
-MeshResource::MeshResource()
-{
-}
-
 MeshResource::~MeshResource()
 {
 	for(Lod& lod : m_lods)

+ 4 - 1
AnKi/Resource/MeshResource.h

@@ -26,7 +26,10 @@ class MeshResource : public ResourceObject
 {
 public:
 	/// Default constructor
-	MeshResource();
+	MeshResource(CString fname, U32 uuid)
+		: ResourceObject(fname, uuid)
+	{
+	}
 
 	~MeshResource();
 

+ 4 - 1
AnKi/Resource/ModelResource.h

@@ -128,7 +128,10 @@ private:
 class ModelResource : public ResourceObject
 {
 public:
-	ModelResource() = default;
+	ModelResource(CString fname, U32 uuid)
+		: ResourceObject(fname, uuid)
+	{
+	}
 
 	~ModelResource() = default;
 

+ 4 - 1
AnKi/Resource/ParticleEmitterResource.h

@@ -103,7 +103,10 @@ public:
 class ParticleEmitterResource : public ResourceObject, private ParticleEmitterProperties
 {
 public:
-	ParticleEmitterResource() = default;
+	ParticleEmitterResource(CString fname, U32 uuid)
+		: ResourceObject(fname, uuid)
+	{
+	}
 
 	~ParticleEmitterResource() = default;
 

+ 89 - 31
AnKi/Resource/ResourceManager.cpp

@@ -38,6 +38,11 @@ ResourceManager::~ResourceManager()
 	deleteInstance(ResourceMemoryPool::getSingleton(), m_transferGpuAlloc);
 	deleteInstance(ResourceMemoryPool::getSingleton(), m_fs);
 
+#define ANKI_INSTANTIATE_RESOURCE(className) \
+	static_cast<TypeData<className>&>(m_allTypes).m_entries.destroy(); \
+	static_cast<TypeData<className>&>(m_allTypes).m_map.destroy();
+#include <AnKi/Resource/Resources.def.h>
+
 	ResourceMemoryPool::freeSingleton();
 }
 
@@ -68,52 +73,105 @@ Error ResourceManager::loadResource(const CString& filename, ResourcePtr<T>& out
 {
 	ANKI_ASSERT(!out.isCreated() && "Already loaded");
 
-	Error err = Error::kNone;
-
-	T* const other = findLoadedResource<T>(filename);
+	TypeData<T>& type = static_cast<TypeData<T>&>(m_allTypes);
 
-	if(other)
+	// Check if registered
+	using EntryType = typename TypeData<T>::Entry;
+	EntryType* entry;
 	{
-		// Found
-		out.reset(other);
+		RLockGuard lock(type.m_mtx);
+		auto it = type.m_map.find(filename);
+		entry = (it != type.m_map.getEnd()) ? &type.m_entries[*it] : nullptr;
 	}
-	else
+
+	if(!entry)
 	{
-		// Allocate ptr
-		T* ptr = newInstance<T>(ResourceMemoryPool::getSingleton());
-		ANKI_ASSERT(ptr->getRefcount() == 0);
+		// Resource entry doesn't exist, create one
 
-		// Increment the refcount in that case where async jobs increment it and decrement it in the scope of a load()
-		ptr->retain();
+		WLockGuard lock(type.m_mtx);
 
-		err = ptr->load(filename, async);
-		if(err)
+		auto it = type.m_map.find(filename); // Search again
+		if(it != type.m_map.getEnd())
 		{
-			ANKI_RESOURCE_LOGE("Failed to load resource: %s", &filename[0]);
-			deleteInstance(ResourceMemoryPool::getSingleton(), ptr);
-			return err;
+			entry = &type.m_entries[*it];
 		}
+		else
+		{
+			auto arrit = type.m_entries.emplace();
+			type.m_map.emplace(filename, arrit.getArrayIndex());
+			entry = &(*arrit);
+		}
+	}
+
+	ANKI_ASSERT(entry);
+
+	// Try loading the resource
+	Error err = Error::kNone;
+	{
+		LockGuard lock(entry->m_mtx);
+
+		if(entry->m_resource == nullptr)
+		{
+			// Resource hasn't been loaded, load it
+
+			T* rsrc = newInstance<T>(ResourceMemoryPool::getSingleton(), filename, m_uuid.fetchAdd(1));
+
+			// Increment the refcount in that case where async jobs increment it and decrement it in the scope of a load()
+			rsrc->retain();
 
-		ptr->setFilename(filename);
-		ptr->setUuid(++m_uuid);
+			err = rsrc->load(filename, async);
 
-		// Register resource
-		registerResource(ptr);
-		out.reset(ptr);
+			// Decrement because of the increment happened a few lines above
+			rsrc->release();
 
-		// Decrement because of the increment happened a few lines above
-		ptr->release();
+			if(err)
+			{
+				ANKI_RESOURCE_LOGE("Failed to load resource: %s", filename.cstr());
+				deleteInstance(ResourceMemoryPool::getSingleton(), rsrc);
+			}
+			else
+			{
+				entry->m_resource = rsrc;
+			}
+		}
+	}
+
+	if(!err)
+	{
+		out.reset(entry->m_resource);
 	}
 
 	return err;
 }
 
-// Instansiate the ResourceManager::loadResource()
-#define ANKI_INSTANTIATE_RESOURCE(rsrc_, ptr_) \
-	template Error ResourceManager::loadResource<rsrc_>(const CString& filename, ResourcePtr<rsrc_>& out, Bool async);
-#define ANKI_INSTANSIATE_RESOURCE_DELIMITER()
-#include <AnKi/Resource/InstantiationMacros.h>
-#undef ANKI_INSTANTIATE_RESOURCE
-#undef ANKI_INSTANSIATE_RESOURCE_DELIMITER
+template<typename T>
+void ResourceManager::freeResource(T* ptr)
+{
+	ANKI_ASSERT(ptr);
+	ANKI_ASSERT(ptr->m_refcount.load() == 0);
+
+	TypeData<T>& type = static_cast<TypeData<T>&>(m_allTypes);
+
+	typename TypeData<T>::Entry* entry = nullptr;
+	{
+		RLockGuard lock(type.m_mtx);
+		auto it = type.m_map.find(ptr->m_fname);
+		ANKI_ASSERT(it != type.m_map.getEnd());
+		entry = &type.m_entries[*it];
+	}
+
+	{
+		LockGuard lock(entry->m_mtx);
+		ANKI_ASSERT(entry->m_resource);
+		deleteInstance(ResourceMemoryPool::getSingleton(), entry->m_resource);
+		entry->m_resource = nullptr;
+	}
+}
+
+// Instansiate
+#define ANKI_INSTANTIATE_RESOURCE(className) \
+	template Error ResourceManager::loadResource<className>(const CString& filename, ResourcePtr<className>& out, Bool async); \
+	template void ResourceManager::freeResource<className>(className * ptr);
+#include <AnKi/Resource/Resources.def.h>
 
 } // end namespace anki

+ 44 - 89
AnKi/Resource/ResourceManager.h

@@ -10,6 +10,8 @@
 #include <AnKi/Util/List.h>
 #include <AnKi/Util/Functions.h>
 #include <AnKi/Util/String.h>
+#include <AnKi/Util/HashMap.h>
+#include <AnKi/Util/BlockArray.h>
 
 namespace anki {
 
@@ -28,74 +30,8 @@ class ShaderProgramResourceSystem;
 inline NumericCVar<PtrSize> g_transferScratchMemorySizeCVar("Rsrc", "TransferScratchMemorySize", 256_MB, 1_MB, 4_GB,
 															"Memory that is used fot texture and buffer uploads");
 
-/// Manage resources of a certain type
-template<typename Type>
-class TypeResourceManager
-{
-protected:
-	TypeResourceManager()
-	{
-	}
-
-	~TypeResourceManager()
-	{
-		ANKI_ASSERT(m_ptrs.isEmpty() && "Forgot to delete some resources");
-		m_ptrs.destroy();
-	}
-
-	Type* findLoadedResource(const CString& filename)
-	{
-		auto it = find(filename);
-		return (it != m_ptrs.end()) ? *it : nullptr;
-	}
-
-	void registerResource(Type* ptr)
-	{
-		ANKI_ASSERT(find(ptr->getFilename()) == m_ptrs.getEnd());
-		m_ptrs.pushBack(ptr);
-	}
-
-	void unregisterResource(Type* ptr)
-	{
-		auto it = find(ptr->getFilename());
-		ANKI_ASSERT(it != m_ptrs.end());
-		m_ptrs.erase(it);
-	}
-
-private:
-	using Container = ResourceList<Type*>;
-
-	Container m_ptrs;
-
-	typename Container::Iterator find(const CString& filename)
-	{
-		typename Container::Iterator it;
-
-		for(it = m_ptrs.getBegin(); it != m_ptrs.getEnd(); ++it)
-		{
-			if((*it)->getFilename() == filename)
-			{
-				break;
-			}
-		}
-
-		return it;
-	}
-};
-
 /// Resource manager. It holds a few global variables
-class ResourceManager : public MakeSingleton<ResourceManager>,
-
-#define ANKI_INSTANTIATE_RESOURCE(rsrc_, ptr_) \
-public \
-	TypeResourceManager<rsrc_>
-
-#define ANKI_INSTANSIATE_RESOURCE_DELIMITER() ,
-
-#include <AnKi/Resource/InstantiationMacros.h>
-#undef ANKI_INSTANTIATE_RESOURCE
-#undef ANKI_INSTANSIATE_RESOURCE_DELIMITER
-
+class ResourceManager : public MakeSingleton<ResourceManager>
 {
 	template<typename T>
 	friend class ResourcePtrDeleter;
@@ -108,32 +44,19 @@ public:
 	Error init(AllocAlignedCallback allocCallback, void* allocCallbackData);
 
 	/// Load a resource.
+	/// @node Thread-safe against itself and freeResource.
 	template<typename T>
 	Error loadResource(const CString& filename, ResourcePtr<T>& out, Bool async = true);
 
 	// Internals:
 
-	ANKI_INTERNAL TransferGpuAllocator& getTransferGpuAllocator()
-	{
-		return *m_transferGpuAlloc;
-	}
-
+	/// @node Thread-safe against itself and loadResource.
 	template<typename T>
-	ANKI_INTERNAL T* findLoadedResource(const CString& filename)
-	{
-		return TypeResourceManager<T>::findLoadedResource(filename);
-	}
-
-	template<typename T>
-	ANKI_INTERNAL void registerResource(T* ptr)
-	{
-		TypeResourceManager<T>::registerResource(ptr);
-	}
+	ANKI_INTERNAL void freeResource(T* ptr);
 
-	template<typename T>
-	ANKI_INTERNAL void unregisterResource(T* ptr)
+	ANKI_INTERNAL TransferGpuAllocator& getTransferGpuAllocator()
 	{
-		TypeResourceManager<T>::unregisterResource(ptr);
+		return *m_transferGpuAlloc;
 	}
 
 	ANKI_INTERNAL AsyncLoader& getAsyncLoader()
@@ -141,9 +64,6 @@ public:
 		return *m_asyncLoader;
 	}
 
-	/// Get the total number of completed async tasks.
-	ANKI_INTERNAL U64 getAsyncTaskCompletedCount() const;
-
 	/// Return the container of program libraries.
 	ANKI_INTERNAL const ShaderProgramResourceSystem& getShaderProgramResourceSystem() const
 	{
@@ -156,12 +76,47 @@ public:
 	}
 
 private:
+	template<typename Type>
+	class TypeData
+	{
+	public:
+		class Entry
+		{
+		public:
+			Type* m_resource = nullptr;
+			SpinLock m_mtx;
+		};
+
+		ResourceHashMap<CString, U32> m_map;
+		ResourceBlockArray<Entry> m_entries;
+		RWMutex m_mtx;
+
+		TypeData()
+		{
+			for([[maybe_unused]] const Entry& e : m_entries)
+			{
+				ANKI_ASSERT(e.m_resource == nullptr && "Forgot to release some resource");
+			}
+		}
+	};
+
+	class AllTypeData:
+#define ANKI_INSTANTIATE_RESOURCE(className) \
+public \
+	TypeData<className>
+#define ANKI_INSTANSIATE_RESOURCE_DELIMITER() ,
+#include <AnKi/Resource/Resources.def.h>
+	{
+	};
+
 	ResourceFilesystem* m_fs = nullptr;
 	AsyncLoader* m_asyncLoader = nullptr; ///< Async loading thread
 	ShaderProgramResourceSystem* m_shaderProgramSystem = nullptr;
 	TransferGpuAllocator* m_transferGpuAlloc = nullptr;
 
-	U64 m_uuid = 0;
+	AllTypeData m_allTypes;
+
+	Atomic<U32> m_uuid = {1};
 
 	ResourceManager();
 

+ 19 - 24
AnKi/Resource/ResourceObject.h

@@ -23,6 +23,14 @@ class ResourceObject
 	friend class ResourcePtrDeleter;
 
 public:
+	ResourceObject(CString fname, U32 uuid)
+		: m_uuid(uuid)
+		, m_fname(fname)
+	{
+		ANKI_ASSERT(uuid > 0);
+		ANKI_ASSERT(!fname.isEmpty());
+	}
+
 	virtual ~ResourceObject() = default;
 
 	void retain() const
@@ -35,48 +43,35 @@ public:
 		return m_refcount.fetchSub(1);
 	}
 
-	I32 getRefcount() const
-	{
-		return m_refcount.load();
-	}
-
 	CString getFilename() const
 	{
 		ANKI_ASSERT(!m_fname.isEmpty());
 		return m_fname.toCString();
 	}
 
-	// Internals:
-
-	ANKI_INTERNAL void setFilename(const CString& fname)
-	{
-		ANKI_ASSERT(m_fname.isEmpty());
-		m_fname = fname;
-	}
-
-	ANKI_INTERNAL void setUuid(U64 uuid)
-	{
-		ANKI_ASSERT(uuid > 0);
-		m_uuid = uuid;
-	}
-
 	/// To check if 2 resource pointers are actually the same resource.
-	ANKI_INTERNAL U64 getUuid() const
+	U32 getUuid() const
 	{
 		ANKI_ASSERT(m_uuid > 0);
 		return m_uuid;
 	}
 
-	ANKI_INTERNAL Error openFile(const ResourceFilename& filename, ResourceFilePtr& file);
+	I32 getRefcount() const
+	{
+		return m_refcount.load();
+	}
+
+protected:
+	Error openFile(const ResourceFilename& filename, ResourceFilePtr& file);
 
-	ANKI_INTERNAL Error openFileReadAllText(const ResourceFilename& filename, ResourceString& file);
+	Error openFileReadAllText(const ResourceFilename& filename, ResourceString& file);
 
-	ANKI_INTERNAL Error openFileParseXml(const ResourceFilename& filename, ResourceXmlDocument& xml);
+	Error openFileParseXml(const ResourceFilename& filename, ResourceXmlDocument& xml);
 
 private:
 	mutable Atomic<I32> m_refcount = {0};
+	U32 m_uuid = 0;
 	ResourceString m_fname; ///< Unique resource name.
-	U64 m_uuid = 0;
 };
 /// @}
 

+ 44 - 0
AnKi/Resource/Resources.def.h

@@ -0,0 +1,44 @@
+// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+// An awful trick to instantiate stuff with the resource type
+
+#if !defined(ANKI_INSTANSIATE_RESOURCE_DELIMITER)
+#	define ANKI_INSTANSIATE_RESOURCE_DELIMITER()
+#endif
+
+ANKI_INSTANTIATE_RESOURCE(AnimationResource)
+ANKI_INSTANSIATE_RESOURCE_DELIMITER()
+ANKI_INSTANTIATE_RESOURCE(ImageResource)
+ANKI_INSTANSIATE_RESOURCE_DELIMITER()
+ANKI_INSTANTIATE_RESOURCE(MeshResource)
+ANKI_INSTANSIATE_RESOURCE_DELIMITER()
+ANKI_INSTANTIATE_RESOURCE(CpuMeshResource)
+ANKI_INSTANSIATE_RESOURCE_DELIMITER()
+ANKI_INSTANTIATE_RESOURCE(SkeletonResource)
+ANKI_INSTANSIATE_RESOURCE_DELIMITER()
+ANKI_INSTANTIATE_RESOURCE(ParticleEmitterResource)
+ANKI_INSTANSIATE_RESOURCE_DELIMITER()
+ANKI_INSTANTIATE_RESOURCE(ModelResource)
+ANKI_INSTANSIATE_RESOURCE_DELIMITER()
+ANKI_INSTANTIATE_RESOURCE(ScriptResource)
+ANKI_INSTANSIATE_RESOURCE_DELIMITER()
+ANKI_INSTANTIATE_RESOURCE(DummyResource)
+ANKI_INSTANSIATE_RESOURCE_DELIMITER()
+ANKI_INSTANTIATE_RESOURCE(GenericResource)
+ANKI_INSTANSIATE_RESOURCE_DELIMITER()
+ANKI_INSTANTIATE_RESOURCE(ImageAtlasResource)
+ANKI_INSTANSIATE_RESOURCE_DELIMITER()
+ANKI_INSTANTIATE_RESOURCE(ShaderProgramResource)
+ANKI_INSTANSIATE_RESOURCE_DELIMITER()
+ANKI_INSTANTIATE_RESOURCE(MaterialResource)
+
+#if defined(ANKI_INSTANTIATE_RESOURCE)
+#	undef ANKI_INSTANTIATE_RESOURCE
+#endif
+
+#if defined(ANKI_INSTANSIATE_RESOURCE_DELIMITER)
+#	undef ANKI_INSTANSIATE_RESOURCE_DELIMITER
+#endif

+ 4 - 1
AnKi/Resource/ScriptResource.h

@@ -16,7 +16,10 @@ namespace anki {
 class ScriptResource : public ResourceObject
 {
 public:
-	ScriptResource() = default;
+	ScriptResource(CString fname, U32 uuid)
+		: ResourceObject(fname, uuid)
+	{
+	}
 
 	~ScriptResource() = default;
 

+ 0 - 4
AnKi/Resource/ShaderProgramResource.cpp

@@ -22,10 +22,6 @@ ShaderProgramResourceVariant::~ShaderProgramResourceVariant()
 {
 }
 
-ShaderProgramResource::ShaderProgramResource()
-{
-}
-
 ShaderProgramResource::~ShaderProgramResource()
 {
 	for(auto it : m_variants)

+ 4 - 1
AnKi/Resource/ShaderProgramResource.h

@@ -103,7 +103,10 @@ private:
 class ShaderProgramResource : public ResourceObject
 {
 public:
-	ShaderProgramResource();
+	ShaderProgramResource(CString fname, U32 uuid)
+		: ResourceObject(fname, uuid)
+	{
+	}
 
 	~ShaderProgramResource();
 

+ 4 - 1
AnKi/Resource/SkeletonResource.h

@@ -89,7 +89,10 @@ private:
 class SkeletonResource : public ResourceObject
 {
 public:
-	SkeletonResource() = default;
+	SkeletonResource(CString fname, U32 uuid)
+		: ResourceObject(fname, uuid)
+	{
+	}
 
 	~SkeletonResource() = default;
 

+ 4 - 3
AnKi/Scene/Components/SceneComponent.h

@@ -94,13 +94,14 @@ public:
 
 	ANKI_INTERNAL U32 getArrayIndex() const
 	{
-		ANKI_ASSERT(m_arrayIdx != kMaxU32);
+		ANKI_ASSERT(m_arrayIdx != kMaxU32 >> 8u);
 		return m_arrayIdx;
 	}
 
 	ANKI_INTERNAL void setArrayIndex(U32 idx)
 	{
-		m_arrayIdx = idx & (kMaxU32 >> 24u);
+		m_arrayIdx = idx & (kMaxU32 >> 8u);
+		ANKI_ASSERT(m_arrayIdx == idx);
 	}
 
 	ANKI_INTERNAL virtual void onDestroy([[maybe_unused]] SceneNode& node)
@@ -133,7 +134,7 @@ private:
 	Timestamp m_timestamp = 1; ///< Indicates when an update happened
 	U32 m_uuid = 0;
 
-	U32 m_arrayIdx : 24 = kMaxU32 >> 24u;
+	U32 m_arrayIdx : 24 = kMaxU32 >> 8u;
 	U32 m_type : 8 = 0; ///< Cache the type ID.
 
 	static constexpr Array<F32, U32(SceneComponentType::kCount)> m_updateOrderWeights = {