Explorar o código

Allow resources to be loaded synchronously

Panagiotis Christopoulos Charitos %!s(int64=8) %!d(string=hai) anos
pai
achega
291e9e885f
Modificáronse 36 ficheiros con 362 adicións e 239 borrados
  1. 5 1
      src/anki/core/Trace.cpp
  2. 4 0
      src/anki/core/Trace.h
  3. 2 0
      src/anki/renderer/Renderer.cpp
  4. 1 1
      src/anki/resource/Animation.cpp
  5. 1 1
      src/anki/resource/Animation.h
  6. 7 1
      src/anki/resource/AsyncLoader.cpp
  7. 1 1
      src/anki/resource/CollisionResource.cpp
  8. 1 1
      src/anki/resource/CollisionResource.h
  9. 1 0
      src/anki/resource/Common.h
  10. 1 1
      src/anki/resource/DummyRsrc.h
  11. 1 1
      src/anki/resource/GenericResource.cpp
  12. 1 1
      src/anki/resource/GenericResource.h
  13. 5 5
      src/anki/resource/Material.cpp
  14. 2 2
      src/anki/resource/Material.h
  15. 98 62
      src/anki/resource/Mesh.cpp
  16. 6 1
      src/anki/resource/Mesh.h
  17. 5 5
      src/anki/resource/Model.cpp
  18. 3 2
      src/anki/resource/Model.h
  19. 2 2
      src/anki/resource/ParticleEmitterResource.cpp
  20. 1 1
      src/anki/resource/ParticleEmitterResource.h
  21. 7 0
      src/anki/resource/ResourceFilesystem.cpp
  22. 1 1
      src/anki/resource/ResourceManager.h
  23. 2 2
      src/anki/resource/ResourceManager.inl.h
  24. 1 1
      src/anki/resource/Script.cpp
  25. 1 1
      src/anki/resource/Script.h
  26. 1 1
      src/anki/resource/ShaderProgramResource.cpp
  27. 1 1
      src/anki/resource/ShaderProgramResource.h
  28. 1 1
      src/anki/resource/Skeleton.cpp
  29. 1 1
      src/anki/resource/Skeleton.h
  30. 2 2
      src/anki/resource/TextureAtlas.cpp
  31. 1 1
      src/anki/resource/TextureAtlas.h
  32. 171 133
      src/anki/resource/TextureResource.cpp
  33. 8 1
      src/anki/resource/TextureResource.h
  34. 3 2
      src/anki/resource/TransferGpuAllocator.h
  35. 3 2
      src/anki/script/LuaBinder.cpp
  36. 10 0
      src/anki/util/Functions.h

+ 5 - 1
src/anki/core/Trace.cpp

@@ -12,6 +12,8 @@ namespace anki
 {
 
 static Array<const char*, U(TraceEventType::COUNT)> eventNames = {{"RESOURCE_ALLOCATE_TRANSFER",
+	"RESOURCE_ASYNC_TASK",
+	"RESOURCE_FILE_READ",
 	"SCENE_UPDATE",
 	"SCENE_DELETE_STUFF",
 	"SCENE_PHYSICS_UPDATE",
@@ -23,6 +25,7 @@ static Array<const char*, U(TraceEventType::COUNT)> eventNames = {{"RESOURCE_ALL
 	"VIS_GATHER_TRIANGLES",
 	"VIS_RASTERIZE",
 	"VIS_RASTERIZER_TEST",
+	"RENDERER_INIT",
 	"RENDER",
 	"RENDER_MS",
 	"RENDER_IS",
@@ -42,7 +45,8 @@ static Array<const char*, U(TraceEventType::COUNT)> eventNames = {{"RESOURCE_ALL
 	"VK_BIND_OBJECT",
 	"VK_DESCRIPTOR_SET_GET_OR_CREATE",
 	"SWAP_BUFFERS",
-	"BARRIER_WAIT"}};
+	"BARRIER_WAIT",
+	"LUA_EXEC"}};
 
 static Array<const char*, U(TraceCounterType::COUNT)> counterNames = {{"GR_DRAWCALLS",
 	"GR_VERTICES",

+ 4 - 0
src/anki/core/Trace.h

@@ -25,6 +25,8 @@ namespace anki
 enum class TraceEventType
 {
 	RESOURCE_ALLOCATE_TRANSFER,
+	RESOURCE_ASYNC_TASK,
+	RESOURCE_FILE_READ,
 	SCENE_UPDATE,
 	SCENE_DELETE_STUFF,
 	SCENE_PHYSICS_UPDATE,
@@ -36,6 +38,7 @@ enum class TraceEventType
 	SCENE_VISIBILITY_GATHER_TRIANGLES,
 	SCENE_VISIBILITY_RASTERIZE,
 	SCENE_RASTERIZER_TEST,
+	RENDERER_INIT,
 	RENDER,
 	RENDER_MS,
 	RENDER_IS,
@@ -56,6 +59,7 @@ enum class TraceEventType
 	VK_DESCRIPTOR_SET_GET_OR_CREATE,
 	SWAP_BUFFERS,
 	BARRIER_WAIT,
+	LUA_EXEC,
 
 	COUNT
 };

+ 2 - 0
src/anki/renderer/Renderer.cpp

@@ -54,6 +54,8 @@ Error Renderer::init(ThreadPool* threadpool,
 	Timestamp* globTimestamp,
 	Bool willDrawToDefaultFbo)
 {
+	ANKI_TRACE_SCOPED_EVENT(RENDERER_INIT);
+
 	m_globTimestamp = globTimestamp;
 	m_threadpool = threadpool;
 	m_resources = resources;

+ 1 - 1
src/anki/resource/Animation.cpp

@@ -24,7 +24,7 @@ Animation::~Animation()
 	m_channels.destroy(getAllocator());
 }
 
-Error Animation::load(const ResourceFilename& filename)
+Error Animation::load(const ResourceFilename& filename, Bool async)
 {
 	XmlElement el;
 	I64 tmp;

+ 1 - 1
src/anki/resource/Animation.h

@@ -71,7 +71,7 @@ public:
 
 	~Animation();
 
-	ANKI_USE_RESULT Error load(const ResourceFilename& filename);
+	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
 
 	/// Get a vector of all animation channels
 	const DynamicArray<AnimationChannel>& getChannels() const

+ 7 - 1
src/anki/resource/AsyncLoader.cpp

@@ -5,6 +5,7 @@
 
 #include <anki/resource/AsyncLoader.h>
 #include <anki/util/Logger.h>
+#include <anki/core/Trace.h>
 
 namespace anki
 {
@@ -122,7 +123,12 @@ Error AsyncLoader::threadWorker()
 			// Exec the task
 			ANKI_ASSERT(task);
 			AsyncLoaderTaskContext ctx;
-			err = (*task)(ctx);
+
+			{
+				ANKI_TRACE_SCOPED_EVENT(RESOURCE_ASYNC_TASK);
+				err = (*task)(ctx);
+			}
+
 			if(!err)
 			{
 				m_completedTaskCount.fetchAdd(1);

+ 1 - 1
src/anki/resource/CollisionResource.cpp

@@ -12,7 +12,7 @@
 namespace anki
 {
 
-Error CollisionResource::load(const ResourceFilename& filename)
+Error CollisionResource::load(const ResourceFilename& filename, Bool async)
 {
 	XmlElement el;
 	XmlDocument doc;

+ 1 - 1
src/anki/resource/CollisionResource.h

@@ -35,7 +35,7 @@ public:
 	{
 	}
 
-	ANKI_USE_RESULT Error load(const ResourceFilename& filename);
+	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
 
 	PhysicsCollisionShapePtr getShape() const
 	{

+ 1 - 0
src/anki/resource/Common.h

@@ -20,6 +20,7 @@ class ResourceManager;
 class ResourceFilesystem;
 template<typename Type>
 class ResourcePointer;
+class TransferGpuAllocatorHandle;
 
 /// @addtogroup resource
 /// @{

+ 1 - 1
src/anki/resource/DummyRsrc.h

@@ -30,7 +30,7 @@ public:
 		}
 	}
 
-	ANKI_USE_RESULT Error load(const ResourceFilename& filename)
+	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async)
 	{
 		Error err = ErrorCode::NONE;
 		if(filename.find("error") == ResourceFilename::NPOS)

+ 1 - 1
src/anki/resource/GenericResource.cpp

@@ -18,7 +18,7 @@ GenericResource::~GenericResource()
 	m_data.destroy(getAllocator());
 }
 
-Error GenericResource::load(const ResourceFilename& filename)
+Error GenericResource::load(const ResourceFilename& filename, Bool async)
 {
 	ResourceFilePtr file;
 	ANKI_CHECK(openFile(filename, file));

+ 1 - 1
src/anki/resource/GenericResource.h

@@ -22,7 +22,7 @@ public:
 	~GenericResource();
 
 	/// Load a texture
-	ANKI_USE_RESULT Error load(const ResourceFilename& filename);
+	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
 
 	const DynamicArray<U8>& getData() const
 	{

+ 5 - 5
src/anki/resource/Material.cpp

@@ -46,7 +46,7 @@ Material::~Material()
 	m_mutations.destroy(getAllocator());
 }
 
-Error Material::load(const ResourceFilename& filename)
+Error Material::load(const ResourceFilename& filename, Bool async)
 {
 	XmlDocument doc;
 	XmlElement el;
@@ -60,7 +60,7 @@ Error Material::load(const ResourceFilename& filename)
 	// shaderProgram
 	CString fname;
 	ANKI_CHECK(rootEl.getAttributeText("shaderProgram", fname));
-	ANKI_CHECK(getManager().loadResource(fname, m_prog));
+	ANKI_CHECK(getManager().loadResource(fname, m_prog, async));
 	m_descriptorSetIdx = m_prog->getDescriptorSetIndex();
 
 	// shadow
@@ -83,7 +83,7 @@ Error Material::load(const ResourceFilename& filename)
 	ANKI_CHECK(rootEl.getChildElementOptional("inputs", el));
 	if(el)
 	{
-		ANKI_CHECK(parseInputs(el));
+		ANKI_CHECK(parseInputs(el, async));
 	}
 
 	return ErrorCode::NONE;
@@ -231,7 +231,7 @@ Error Material::parseMutators(XmlElement mutatorsEl)
 	return ErrorCode::NONE;
 }
 
-Error Material::parseInputs(XmlElement inputsEl)
+Error Material::parseInputs(XmlElement inputsEl, Bool async)
 {
 	// Iterate the program's variables and get counts
 	U constInputCount = 0;
@@ -439,7 +439,7 @@ Error Material::parseInputs(XmlElement inputsEl)
 				{
 					CString texfname;
 					ANKI_CHECK(inputEl.getAttributeText("value", texfname));
-					ANKI_CHECK(getManager().loadResource(texfname, mtlVar.m_tex));
+					ANKI_CHECK(getManager().loadResource(texfname, mtlVar.m_tex, async));
 					break;
 				}
 

+ 2 - 2
src/anki/resource/Material.h

@@ -206,7 +206,7 @@ public:
 	~Material();
 
 	/// Load a material file
-	ANKI_USE_RESULT Error load(const ResourceFilename& filename);
+	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
 
 	U getLodCount() const
 	{
@@ -274,7 +274,7 @@ private:
 	static U getInstanceGroupIdx(U instanceCount);
 
 	/// Parse whatever is inside the <inputs> tag.
-	ANKI_USE_RESULT Error parseInputs(XmlElement inputsEl);
+	ANKI_USE_RESULT Error parseInputs(XmlElement inputsEl, Bool async);
 
 	ANKI_USE_RESULT Error parseMutators(XmlElement mutatorsEl);
 };

+ 98 - 62
src/anki/resource/Mesh.cpp

@@ -13,8 +13,7 @@
 namespace anki
 {
 
-/// Mesh upload async task.
-class MeshLoadTask : public AsyncLoaderTask
+class Mesh::LoadContext
 {
 public:
 	ResourceManager* m_manager ANKI_DBG_NULLIFY;
@@ -22,72 +21,29 @@ public:
 	BufferPtr m_vertBuff;
 	BufferPtr m_indicesBuff;
 
-	MeshLoadTask(ResourceManager* manager)
+	LoadContext(ResourceManager* manager, GenericMemoryPoolAllocator<U8> alloc)
 		: m_manager(manager)
-		, m_loader(manager, manager->getAsyncLoader().getAllocator())
+		, m_loader(manager, alloc)
 	{
 	}
-
-	Error operator()(AsyncLoaderTaskContext& ctx) final;
 };
 
-Error MeshLoadTask::operator()(AsyncLoaderTaskContext& ctx)
+/// Mesh upload async task.
+class Mesh::LoadTask : public AsyncLoaderTask
 {
-	GrManager& gr = m_manager->getGrManager();
-	TransferGpuAllocator& transferAlloc = m_manager->getTransferGpuAllocator();
-	Array<TransferGpuAllocatorHandle, 2> handles;
-
-	CommandBufferInitInfo cmdbinit;
-	cmdbinit.m_flags = CommandBufferFlag::SMALL_BATCH | CommandBufferFlag::TRANSFER_WORK;
-	CommandBufferPtr cmdb = gr.newInstance<CommandBuffer>(cmdbinit);
+public:
+	Mesh::LoadContext m_ctx;
 
-	// Write vert buff
+	LoadTask(ResourceManager* manager)
+		: m_ctx(manager, manager->getAsyncLoader().getAllocator())
 	{
-		ANKI_CHECK(transferAlloc.allocate(m_loader.getVertexDataSize(), handles[0]));
-		void* data = handles[0].getMappedMemory();
-		ANKI_ASSERT(data);
-
-		memcpy(data, m_loader.getVertexData(), m_loader.getVertexDataSize());
-
-		cmdb->setBufferBarrier(
-			m_vertBuff, BufferUsageBit::VERTEX, BufferUsageBit::BUFFER_UPLOAD_DESTINATION, 0, MAX_PTR_SIZE);
-
-		cmdb->copyBufferToBuffer(handles[0].getBuffer(), handles[0].getOffset(), m_vertBuff, 0, handles[0].getRange());
-
-		cmdb->setBufferBarrier(
-			m_vertBuff, BufferUsageBit::BUFFER_UPLOAD_DESTINATION, BufferUsageBit::VERTEX, 0, MAX_PTR_SIZE);
-
-		m_vertBuff.reset(nullptr);
 	}
 
-	// Create index buffer
+	Error operator()(AsyncLoaderTaskContext& ctx) final
 	{
-		ANKI_CHECK(transferAlloc.allocate(m_loader.getIndexDataSize(), handles[1]));
-		void* data = handles[1].getMappedMemory();
-		ANKI_ASSERT(data);
-
-		memcpy(data, m_loader.getIndexData(), m_loader.getIndexDataSize());
-
-		cmdb->setBufferBarrier(
-			m_indicesBuff, BufferUsageBit::INDEX, BufferUsageBit::BUFFER_UPLOAD_DESTINATION, 0, MAX_PTR_SIZE);
-
-		cmdb->copyBufferToBuffer(
-			handles[1].getBuffer(), handles[1].getOffset(), m_indicesBuff, 0, handles[1].getRange());
-
-		cmdb->setBufferBarrier(
-			m_indicesBuff, BufferUsageBit::BUFFER_UPLOAD_DESTINATION, BufferUsageBit::INDEX, 0, MAX_PTR_SIZE);
-
-		m_indicesBuff.reset(nullptr);
+		return Mesh::load(m_ctx);
 	}
-
-	FencePtr fence;
-	cmdb->flush(&fence);
-
-	transferAlloc.release(handles[0], fence);
-	transferAlloc.release(handles[1], fence);
-
-	return ErrorCode::NONE;
-}
+};
 
 Mesh::Mesh(ResourceManager* manager)
 	: ResourceObject(manager)
@@ -105,11 +61,24 @@ Bool Mesh::isCompatible(const Mesh& other) const
 		&& m_texChannelsCount == other.m_texChannelsCount;
 }
 
-Error Mesh::load(const ResourceFilename& filename)
+Error Mesh::load(const ResourceFilename& filename, Bool async)
 {
-	MeshLoadTask* task = getManager().getAsyncLoader().newTask<MeshLoadTask>(&getManager());
+	LoadTask* task;
+	LoadContext* ctx;
+	LoadContext localCtx(&getManager(), getTempAllocator());
+
+	if(async)
+	{
+		task = getManager().getAsyncLoader().newTask<LoadTask>(&getManager());
+		ctx = &task->m_ctx;
+	}
+	else
+	{
+		task = nullptr;
+		ctx = &localCtx;
+	}
 
-	MeshLoader& loader = task->m_loader;
+	MeshLoader& loader = ctx->m_loader;
 	ANKI_CHECK(loader.load(filename));
 
 	const MeshLoader::Header& header = loader.getHeader();
@@ -153,9 +122,76 @@ Error Mesh::load(const ResourceFilename& filename)
 	cmdb->flush();
 
 	// Submit the loading task
-	task->m_indicesBuff = m_indicesBuff;
-	task->m_vertBuff = m_vertBuff;
-	getManager().getAsyncLoader().submitTask(task);
+	ctx->m_vertBuff = m_vertBuff;
+	ctx->m_indicesBuff = m_indicesBuff;
+
+	if(async)
+	{
+		getManager().getAsyncLoader().submitTask(task);
+	}
+	else
+	{
+		ANKI_CHECK(load(*ctx));
+	}
+
+	return ErrorCode::NONE;
+}
+
+Error Mesh::load(LoadContext& ctx)
+{
+	GrManager& gr = ctx.m_manager->getGrManager();
+	TransferGpuAllocator& transferAlloc = ctx.m_manager->getTransferGpuAllocator();
+	const MeshLoader& loader = ctx.m_loader;
+	Array<TransferGpuAllocatorHandle, 2> handles;
+
+	CommandBufferInitInfo cmdbinit;
+	cmdbinit.m_flags = CommandBufferFlag::SMALL_BATCH | CommandBufferFlag::TRANSFER_WORK;
+	CommandBufferPtr cmdb = gr.newInstance<CommandBuffer>(cmdbinit);
+
+	// Set barriers
+	cmdb->setBufferBarrier(
+		ctx.m_vertBuff, BufferUsageBit::VERTEX, BufferUsageBit::BUFFER_UPLOAD_DESTINATION, 0, MAX_PTR_SIZE);
+	cmdb->setBufferBarrier(
+		ctx.m_indicesBuff, BufferUsageBit::INDEX, BufferUsageBit::BUFFER_UPLOAD_DESTINATION, 0, MAX_PTR_SIZE);
+
+	// Write vert buff
+	{
+		ANKI_CHECK(transferAlloc.allocate(loader.getVertexDataSize(), handles[0]));
+		void* data = handles[0].getMappedMemory();
+		ANKI_ASSERT(data);
+
+		memcpy(data, loader.getVertexData(), loader.getVertexDataSize());
+
+		cmdb->copyBufferToBuffer(
+			handles[0].getBuffer(), handles[0].getOffset(), ctx.m_vertBuff, 0, handles[0].getRange());
+	}
+
+	// Create index buffer
+	{
+		ANKI_CHECK(transferAlloc.allocate(loader.getIndexDataSize(), handles[1]));
+		void* data = handles[1].getMappedMemory();
+		ANKI_ASSERT(data);
+
+		memcpy(data, loader.getIndexData(), loader.getIndexDataSize());
+
+		cmdb->copyBufferToBuffer(
+			handles[1].getBuffer(), handles[1].getOffset(), ctx.m_indicesBuff, 0, handles[1].getRange());
+	}
+
+	// Set barriers
+	cmdb->setBufferBarrier(
+		ctx.m_vertBuff, BufferUsageBit::BUFFER_UPLOAD_DESTINATION, BufferUsageBit::VERTEX, 0, MAX_PTR_SIZE);
+	cmdb->setBufferBarrier(
+		ctx.m_indicesBuff, BufferUsageBit::BUFFER_UPLOAD_DESTINATION, BufferUsageBit::INDEX, 0, MAX_PTR_SIZE);
+
+	ctx.m_vertBuff.reset(nullptr);
+	ctx.m_indicesBuff.reset(nullptr);
+
+	FencePtr fence;
+	cmdb->flush(&fence);
+
+	transferAlloc.release(handles[0], fence);
+	transferAlloc.release(handles[1], fence);
 
 	return ErrorCode::NONE;
 }

+ 6 - 1
src/anki/resource/Mesh.h

@@ -87,9 +87,12 @@ public:
 	Bool isCompatible(const Mesh& other) const;
 
 	/// Load from a mesh file
-	ANKI_USE_RESULT Error load(const ResourceFilename& filename);
+	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
 
 protected:
+	class LoadTask;
+	class LoadContext;
+
 	/// Per sub mesh data
 	struct SubMesh
 	{
@@ -107,6 +110,8 @@ protected:
 
 	BufferPtr m_vertBuff;
 	BufferPtr m_indicesBuff;
+
+	static ANKI_USE_RESULT Error load(LoadContext& ctx);
 };
 /// @}
 

+ 5 - 5
src/anki/resource/Model.cpp

@@ -97,18 +97,18 @@ U ModelPatch::getLodCount() const
 	return max<U>(m_meshCount, getMaterial()->getLodCount());
 }
 
-Error ModelPatch::create(WeakArray<CString> meshFNames, const CString& mtlFName, ResourceManager* manager)
+Error ModelPatch::create(WeakArray<CString> meshFNames, const CString& mtlFName, Bool async, ResourceManager* manager)
 {
 	ANKI_ASSERT(meshFNames.getSize() > 0);
 
 	// Load material
-	ANKI_CHECK(manager->loadResource(mtlFName, m_mtl));
+	ANKI_CHECK(manager->loadResource(mtlFName, m_mtl, async));
 
 	// Load meshes
 	m_meshCount = 0;
 	for(U i = 0; i < meshFNames.getSize(); i++)
 	{
-		ANKI_CHECK(manager->loadResource(meshFNames[i], m_meshes[i]));
+		ANKI_CHECK(manager->loadResource(meshFNames[i], m_meshes[i], async));
 
 		// Sanity check
 		if(i > 0 && !m_meshes[i]->isCompatible(*m_meshes[i - 1]))
@@ -140,7 +140,7 @@ Model::~Model()
 	m_modelPatches.destroy(alloc);
 }
 
-Error Model::load(const ResourceFilename& filename)
+Error Model::load(const ResourceFilename& filename, Bool async)
 {
 	auto alloc = getAllocator();
 
@@ -221,7 +221,7 @@ Error Model::load(const ResourceFilename& filename)
 			return ErrorCode::OUT_OF_MEMORY;
 		}
 
-		ANKI_CHECK(mpatch->create(WeakArray<CString>(&meshesFnames[0], meshesCount), cstr, &getManager()));
+		ANKI_CHECK(mpatch->create(WeakArray<CString>(&meshesFnames[0], meshesCount), cstr, async, &getManager()));
 
 		m_modelPatches[count++] = mpatch;
 

+ 3 - 2
src/anki/resource/Model.h

@@ -115,7 +115,8 @@ public:
 		return m_meshes[0]->getSubMeshesCount();
 	}
 
-	ANKI_USE_RESULT Error create(WeakArray<CString> meshFNames, const CString& mtlFName, ResourceManager* resources);
+	ANKI_USE_RESULT Error create(
+		WeakArray<CString> meshFNames, const CString& mtlFName, Bool async, ResourceManager* resources);
 
 	/// Get information for multiDraw rendering. Given an array of submeshes that are visible return the correct indices
 	/// offsets and counts.
@@ -176,7 +177,7 @@ public:
 		return m_visibilityShape;
 	}
 
-	ANKI_USE_RESULT Error load(const ResourceFilename& filename);
+	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
 
 private:
 	DynamicArray<ModelPatch*> m_modelPatches;

+ 2 - 2
src/anki/resource/ParticleEmitterResource.cpp

@@ -95,7 +95,7 @@ ParticleEmitterResource::~ParticleEmitterResource()
 {
 }
 
-Error ParticleEmitterResource::load(const ResourceFilename& filename)
+Error ParticleEmitterResource::load(const ResourceFilename& filename, Bool async)
 {
 	U32 tmp;
 
@@ -146,7 +146,7 @@ Error ParticleEmitterResource::load(const ResourceFilename& filename)
 	CString cstr;
 	ANKI_CHECK(rel.getChildElement("material", el));
 	ANKI_CHECK(el.getText(cstr));
-	ANKI_CHECK(getManager().loadResource(cstr, m_material));
+	ANKI_CHECK(getManager().loadResource(cstr, m_material, async));
 
 	// sanity checks
 	//

+ 1 - 1
src/anki/resource/ParticleEmitterResource.h

@@ -115,7 +115,7 @@ public:
 	void getRenderingInfo(U lod, ShaderProgramPtr& prog) const;
 
 	/// Load it
-	ANKI_USE_RESULT Error load(const ResourceFilename& filename);
+	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
 
 private:
 	MaterialResourcePtr m_material;

+ 7 - 0
src/anki/resource/ResourceFilesystem.cpp

@@ -6,6 +6,7 @@
 #include <anki/resource/ResourceFilesystem.h>
 #include <anki/util/Filesystem.h>
 #include <anki/misc/ConfigSet.h>
+#include <anki/core/Trace.h>
 #include <contrib/minizip/unzip.h>
 
 namespace anki
@@ -24,21 +25,25 @@ public:
 
 	ANKI_USE_RESULT Error read(void* buff, PtrSize size) override
 	{
+		ANKI_TRACE_SCOPED_EVENT(RESOURCE_FILE_READ);
 		return m_file.read(buff, size);
 	}
 
 	ANKI_USE_RESULT Error readAllText(GenericMemoryPoolAllocator<U8> alloc, String& out) override
 	{
+		ANKI_TRACE_SCOPED_EVENT(RESOURCE_FILE_READ);
 		return m_file.readAllText(alloc, out);
 	}
 
 	ANKI_USE_RESULT Error readU32(U32& u) override
 	{
+		ANKI_TRACE_SCOPED_EVENT(RESOURCE_FILE_READ);
 		return m_file.readU32(u);
 	}
 
 	ANKI_USE_RESULT Error readF32(F32& f) override
 	{
+		ANKI_TRACE_SCOPED_EVENT(RESOURCE_FILE_READ);
 		return m_file.readF32(f);
 	}
 
@@ -123,6 +128,8 @@ public:
 
 	ANKI_USE_RESULT Error read(void* buff, PtrSize size) override
 	{
+		ANKI_TRACE_SCOPED_EVENT(RESOURCE_FILE_READ);
+
 		I64 readSize = unzReadCurrentFile(m_archive, buff, size);
 
 		if(I64(size) != readSize)

+ 1 - 1
src/anki/resource/ResourceManager.h

@@ -126,7 +126,7 @@ public:
 
 	/// Load a resource.
 	template<typename T>
-	ANKI_USE_RESULT Error loadResource(const CString& filename, ResourcePtr<T>& out);
+	ANKI_USE_RESULT Error loadResource(const CString& filename, ResourcePtr<T>& out, Bool async = true);
 
 anki_internal:
 	U32 getMaxTextureSize() const

+ 2 - 2
src/anki/resource/ResourceManager.inl.h

@@ -9,7 +9,7 @@ namespace anki
 {
 
 template<typename T>
-Error ResourceManager::loadResource(const CString& filename, ResourcePtr<T>& out)
+Error ResourceManager::loadResource(const CString& filename, ResourcePtr<T>& out, Bool async)
 {
 	ANKI_ASSERT(!out.isCreated() && "Already loaded");
 
@@ -36,7 +36,7 @@ Error ResourceManager::loadResource(const CString& filename, ResourcePtr<T>& out
 			U allocsCountBefore = pool.getAllocationsCount();
 			(void)allocsCountBefore;
 
-			err = ptr->load(filename);
+			err = ptr->load(filename, async);
 			if(err)
 			{
 				ANKI_RESOURCE_LOGE("Failed to load resource: %s", &filename[0]);

+ 1 - 1
src/anki/resource/Script.cpp

@@ -14,7 +14,7 @@ Script::~Script()
 	m_source.destroy(getAllocator());
 }
 
-Error Script::load(const ResourceFilename& filename)
+Error Script::load(const ResourceFilename& filename, Bool async)
 {
 	ResourceFilePtr file;
 	ANKI_CHECK(openFile(filename, file));

+ 1 - 1
src/anki/resource/Script.h

@@ -24,7 +24,7 @@ public:
 
 	~Script();
 
-	ANKI_USE_RESULT Error load(const ResourceFilename& filename);
+	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
 
 	CString getSource() const
 	{

+ 1 - 1
src/anki/resource/ShaderProgramResource.cpp

@@ -186,7 +186,7 @@ ShaderProgramResource::~ShaderProgramResource()
 	}
 }
 
-Error ShaderProgramResource::load(const ResourceFilename& filename)
+Error ShaderProgramResource::load(const ResourceFilename& filename, Bool async)
 {
 	XmlElement el;
 	XmlDocument doc;

+ 1 - 1
src/anki/resource/ShaderProgramResource.h

@@ -321,7 +321,7 @@ public:
 	~ShaderProgramResource();
 
 	/// Load the resource.
-	ANKI_USE_RESULT Error load(const ResourceFilename& filename);
+	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
 
 	/// Get the array of input variables.
 	const DynamicArray<ShaderProgramResourceInputVariable>& getInputVariables() const

+ 1 - 1
src/anki/resource/Skeleton.cpp

@@ -20,7 +20,7 @@ Skeleton::~Skeleton()
 	m_bones.destroy(getAllocator());
 }
 
-Error Skeleton::load(const ResourceFilename& filename)
+Error Skeleton::load(const ResourceFilename& filename, Bool async)
 {
 	XmlDocument doc;
 	ANKI_CHECK(openFileParseXml(filename, doc));

+ 1 - 1
src/anki/resource/Skeleton.h

@@ -76,7 +76,7 @@ public:
 	~Skeleton();
 
 	/// Load file
-	ANKI_USE_RESULT Error load(const ResourceFilename& filename);
+	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
 
 	const DynamicArray<Bone>& getBones() const
 	{

+ 2 - 2
src/anki/resource/TextureAtlas.cpp

@@ -21,7 +21,7 @@ TextureAtlas::~TextureAtlas()
 	m_subTexNames.destroy(getAllocator());
 }
 
-Error TextureAtlas::load(const ResourceFilename& filename)
+Error TextureAtlas::load(const ResourceFilename& filename, Bool async)
 {
 	XmlDocument doc;
 	ANKI_CHECK(openFileParseXml(filename, doc));
@@ -39,7 +39,7 @@ Error TextureAtlas::load(const ResourceFilename& filename)
 	ANKI_CHECK(rootel.getChildElement("texture", el));
 	CString texFname;
 	ANKI_CHECK(el.getText(texFname));
-	ANKI_CHECK(getManager().loadResource<TextureResource>(texFname, m_tex));
+	ANKI_CHECK(getManager().loadResource<TextureResource>(texFname, m_tex, async));
 
 	m_size[0] = m_tex->getWidth();
 	m_size[1] = m_tex->getHeight();

+ 1 - 1
src/anki/resource/TextureAtlas.h

@@ -40,7 +40,7 @@ public:
 	~TextureAtlas();
 
 	/// Load a texture atlas.
-	ANKI_USE_RESULT Error load(const ResourceFilename& filename);
+	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
 
 	TexturePtr getGrTexture() const
 	{

+ 171 - 133
src/anki/resource/TextureResource.cpp

@@ -11,161 +11,68 @@
 namespace anki
 {
 
-/// Texture upload async task.
-class TexUploadTask : public AsyncLoaderTask
+class TextureResource::LoadingContext
 {
 public:
-	static constexpr U MAX_COPIES_BEFORE_FLUSH = 8;
-
 	ImageLoader m_loader;
-	TexturePtr m_tex;
-	GrManager* m_gr ANKI_DBG_NULLIFY;
-	TransferGpuAllocator* m_transferAlloc ANKI_DBG_NULLIFY;
-	U m_layers = 0;
 	U m_faces = 0;
+	U m_layerCount = 0;
+	GrManager* m_gr ANKI_DBG_NULLIFY;
+	TransferGpuAllocator* m_trfAlloc ANKI_DBG_NULLIFY;
 	TextureType m_texType;
+	TexturePtr m_tex;
 
-	TexUploadTask(GenericMemoryPoolAllocator<U8> alloc)
+	LoadingContext(GenericMemoryPoolAllocator<U8> alloc)
 		: m_loader(alloc)
 	{
 	}
-
-	Error operator()(AsyncLoaderTaskContext& ctx) final;
-
-	void flush(WeakArray<TransferGpuAllocatorHandle> handles, CommandBufferPtr& cmdb);
 };
 
-void TexUploadTask::flush(WeakArray<TransferGpuAllocatorHandle> handles, CommandBufferPtr& cmdb)
+/// Texture upload async task.
+class TextureResource::TexUploadTask : public AsyncLoaderTask
 {
-	FencePtr fence;
-	cmdb->flush(&fence);
+public:
+	TextureResource::LoadingContext m_ctx;
 
-	for(TransferGpuAllocatorHandle& handle : handles)
+	TexUploadTask(GenericMemoryPoolAllocator<U8> alloc)
+		: m_ctx(alloc)
 	{
-		m_transferAlloc->release(handle, fence);
 	}
 
-	cmdb.reset(nullptr);
-}
+	Error operator()(AsyncLoaderTaskContext& ctx) final
+	{
+		return TextureResource::load(m_ctx);
+	}
+};
 
-Error TexUploadTask::operator()(AsyncLoaderTaskContext& ctx)
+TextureResource::~TextureResource()
 {
-	CommandBufferPtr cmdb;
+}
 
-	Array<TransferGpuAllocatorHandle, MAX_COPIES_BEFORE_FLUSH> handles;
-	U handleCount = 0;
+Error TextureResource::load(const ResourceFilename& filename, Bool async)
+{
+	TexUploadTask* task;
+	LoadingContext* ctx;
+	LoadingContext localCtx(getTempAllocator());
 
-	// Upload the data
-	for(U layer = 0; layer < m_layers; ++layer)
+	if(async)
 	{
-		for(U face = 0; face < m_faces; ++face)
-		{
-			for(U mip = 0; mip < m_loader.getMipLevelsCount(); ++mip)
-			{
-				if(!cmdb)
-				{
-					CommandBufferInitInfo ci;
-					ci.m_flags = CommandBufferFlag::TRANSFER_WORK | CommandBufferFlag::SMALL_BATCH;
-					cmdb = m_gr->newInstance<CommandBuffer>(ci);
-				}
-
-				PtrSize surfOrVolSize;
-				const void* surfOrVolData;
-				PtrSize allocationSize;
-
-				if(m_texType == TextureType::_3D)
-				{
-					const auto& vol = m_loader.getVolume(mip);
-					surfOrVolSize = vol.m_data.getSize();
-					surfOrVolData = &vol.m_data[0];
-
-					m_gr->getTextureVolumeUploadInfo(m_tex, TextureVolumeInfo(mip), allocationSize);
-				}
-				else
-				{
-					const auto& surf = m_loader.getSurface(mip, face, layer);
-					surfOrVolSize = surf.m_data.getSize();
-					surfOrVolData = &surf.m_data[0];
-
-					m_gr->getTextureSurfaceUploadInfo(m_tex, TextureSurfaceInfo(mip, 0, face, layer), allocationSize);
-				}
-
-				ANKI_ASSERT(allocationSize >= surfOrVolSize);
-
-				TransferGpuAllocatorHandle& handle = handles[handleCount++];
-				ANKI_CHECK(m_transferAlloc->allocate(allocationSize, handle));
-				void* data = handle.getMappedMemory();
-				ANKI_ASSERT(data);
-
-				memcpy(data, surfOrVolData, surfOrVolSize);
-
-				if(m_texType == TextureType::_3D)
-				{
-					TextureVolumeInfo vol(mip);
-
-					cmdb->setTextureVolumeBarrier(
-						m_tex, TextureUsageBit::NONE, TextureUsageBit::TRANSFER_DESTINATION, vol);
-
-					cmdb->copyBufferToTextureVolume(
-						handle.getBuffer(), handle.getOffset(), handle.getRange(), m_tex, vol);
-
-					cmdb->setTextureVolumeBarrier(m_tex,
-						TextureUsageBit::TRANSFER_DESTINATION,
-						TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::SAMPLED_TESSELLATION_EVALUATION,
-						vol);
-				}
-				else
-				{
-					TextureSurfaceInfo surf(mip, 0, face, layer);
-
-					cmdb->setTextureSurfaceBarrier(
-						m_tex, TextureUsageBit::NONE, TextureUsageBit::TRANSFER_DESTINATION, surf);
-
-					cmdb->copyBufferToTextureSurface(
-						handle.getBuffer(), handle.getOffset(), handle.getRange(), m_tex, surf);
-
-					cmdb->setTextureSurfaceBarrier(m_tex,
-						TextureUsageBit::TRANSFER_DESTINATION,
-						TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::SAMPLED_TESSELLATION_EVALUATION,
-						surf);
-				}
-
-				// Check if you should flush the batch
-				if(handleCount == handles.getSize())
-				{
-					flush({&handles[0], handleCount}, cmdb);
-					handleCount = 0;
-				}
-			}
-		}
+		task = getManager().getAsyncLoader().newTask<TexUploadTask>(getManager().getAsyncLoader().getAllocator());
+		ctx = &task->m_ctx;
 	}
-
-	// Enque what remains
-	if(handleCount)
+	else
 	{
-		flush({&handles[0], handleCount}, cmdb);
+		task = nullptr;
+		ctx = &localCtx;
 	}
+	ImageLoader& loader = ctx->m_loader;
 
-	return ErrorCode::NONE;
-}
-
-TextureResource::~TextureResource()
-{
-}
-
-Error TextureResource::load(const ResourceFilename& filename)
-{
 	TextureInitInfo init;
 	init.m_usage = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::SAMPLED_TESSELLATION_EVALUATION
 		| TextureUsageBit::TRANSFER_DESTINATION;
 	init.m_usageWhenEncountered = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::SAMPLED_TESSELLATION_EVALUATION;
 	U faces = 0;
 
-	// Load image
-	TexUploadTask* task =
-		getManager().getAsyncLoader().newTask<TexUploadTask>(getManager().getAsyncLoader().getAllocator());
-	ImageLoader& loader = task->m_loader;
-
 	ResourceFilePtr file;
 	ANKI_CHECK(openFile(filename, file));
 
@@ -269,20 +176,151 @@ Error TextureResource::load(const ResourceFilename& filename)
 	// Create the texture
 	m_tex = getManager().getGrManager().newInstance<Texture>(init);
 
-	// Upload the data asynchronously
-	task->m_layers = init.m_layerCount;
-	task->m_faces = faces;
-	task->m_gr = &getManager().getGrManager();
-	task->m_transferAlloc = &getManager().getTransferGpuAllocator();
-	task->m_tex = m_tex;
-	task->m_texType = init.m_type;
+	// Set the context
+	ctx->m_faces = faces;
+	ctx->m_layerCount = init.m_layerCount;
+	ctx->m_gr = &getManager().getGrManager();
+	ctx->m_trfAlloc = &getManager().getTransferGpuAllocator();
+	ctx->m_texType = init.m_type;
+	ctx->m_tex = m_tex;
 
-	getManager().getAsyncLoader().submitTask(task);
+	// Upload the data
+	if(async)
+	{
+		getManager().getAsyncLoader().submitTask(task);
+	}
+	else
+	{
+		ANKI_CHECK(load(*ctx));
+	}
 
-	// Done
 	m_size = UVec3(init.m_width, init.m_height, init.m_depth);
 	m_layerCount = init.m_layerCount;
 	return ErrorCode::NONE;
 }
 
+Error TextureResource::load(LoadingContext& ctx)
+{
+	const U copyCount = ctx.m_layerCount * ctx.m_faces * ctx.m_loader.getMipLevelsCount();
+
+	for(U b = 0; b < copyCount; b += MAX_COPIES_BEFORE_FLUSH)
+	{
+		const U begin = b;
+		const U end = min(copyCount, b + MAX_COPIES_BEFORE_FLUSH);
+
+		CommandBufferInitInfo ci;
+		ci.m_flags = CommandBufferFlag::TRANSFER_WORK | CommandBufferFlag::SMALL_BATCH;
+		CommandBufferPtr cmdb = ctx.m_gr->newInstance<CommandBuffer>(ci);
+
+		// Set the barriers of the batch
+		for(U i = begin; i < end; ++i)
+		{
+			U mip, layer, face;
+			unflatten3dArrayIndex(ctx.m_layerCount, ctx.m_faces, ctx.m_loader.getMipLevelsCount(), i, layer, face, mip);
+
+			if(ctx.m_texType == TextureType::_3D)
+			{
+				TextureVolumeInfo vol(mip);
+				cmdb->setTextureVolumeBarrier(
+					ctx.m_tex, TextureUsageBit::NONE, TextureUsageBit::TRANSFER_DESTINATION, vol);
+			}
+			else
+			{
+				TextureSurfaceInfo surf(mip, 0, face, layer);
+				cmdb->setTextureSurfaceBarrier(
+					ctx.m_tex, TextureUsageBit::NONE, TextureUsageBit::TRANSFER_DESTINATION, surf);
+			}
+		}
+
+		// Do the copies
+		Array<TransferGpuAllocatorHandle, MAX_COPIES_BEFORE_FLUSH> handles;
+		U handleCount = 0;
+		for(U i = begin; i < end; ++i)
+		{
+			U mip, layer, face;
+			unflatten3dArrayIndex(ctx.m_layerCount, ctx.m_faces, ctx.m_loader.getMipLevelsCount(), i, layer, face, mip);
+
+			PtrSize surfOrVolSize;
+			const void* surfOrVolData;
+			PtrSize allocationSize;
+
+			if(ctx.m_texType == TextureType::_3D)
+			{
+				const auto& vol = ctx.m_loader.getVolume(mip);
+				surfOrVolSize = vol.m_data.getSize();
+				surfOrVolData = &vol.m_data[0];
+
+				ctx.m_gr->getTextureVolumeUploadInfo(ctx.m_tex, TextureVolumeInfo(mip), allocationSize);
+			}
+			else
+			{
+				const auto& surf = ctx.m_loader.getSurface(mip, face, layer);
+				surfOrVolSize = surf.m_data.getSize();
+				surfOrVolData = &surf.m_data[0];
+
+				ctx.m_gr->getTextureSurfaceUploadInfo(
+					ctx.m_tex, TextureSurfaceInfo(mip, 0, face, layer), allocationSize);
+			}
+
+			ANKI_ASSERT(allocationSize >= surfOrVolSize);
+			TransferGpuAllocatorHandle& handle = handles[handleCount++];
+			ANKI_CHECK(ctx.m_trfAlloc->allocate(allocationSize, handle));
+			void* data = handle.getMappedMemory();
+			ANKI_ASSERT(data);
+
+			memcpy(data, surfOrVolData, surfOrVolSize);
+
+			if(ctx.m_texType == TextureType::_3D)
+			{
+				TextureVolumeInfo vol(mip);
+				cmdb->copyBufferToTextureVolume(
+					handle.getBuffer(), handle.getOffset(), handle.getRange(), ctx.m_tex, vol);
+			}
+			else
+			{
+				TextureSurfaceInfo surf(mip, 0, face, layer);
+
+				cmdb->copyBufferToTextureSurface(
+					handle.getBuffer(), handle.getOffset(), handle.getRange(), ctx.m_tex, surf);
+			}
+		}
+
+		// Set the barriers of the batch
+		for(U i = begin; i < end; ++i)
+		{
+			U mip, layer, face;
+			unflatten3dArrayIndex(ctx.m_layerCount, ctx.m_faces, ctx.m_loader.getMipLevelsCount(), i, layer, face, mip);
+
+			if(ctx.m_texType == TextureType::_3D)
+			{
+				TextureVolumeInfo vol(mip);
+				cmdb->setTextureVolumeBarrier(ctx.m_tex,
+					TextureUsageBit::TRANSFER_DESTINATION,
+					TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::SAMPLED_TESSELLATION_EVALUATION,
+					vol);
+			}
+			else
+			{
+				TextureSurfaceInfo surf(mip, 0, face, layer);
+				cmdb->setTextureSurfaceBarrier(ctx.m_tex,
+					TextureUsageBit::TRANSFER_DESTINATION,
+					TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::SAMPLED_TESSELLATION_EVALUATION,
+					surf);
+			}
+		}
+
+		// Flush batch
+		FencePtr fence;
+		cmdb->flush(&fence);
+
+		for(U i = 0; i < handleCount; ++i)
+		{
+			ctx.m_trfAlloc->release(handles[i], fence);
+		}
+		cmdb.reset(nullptr);
+	}
+
+	return ErrorCode::NONE;
+}
+
 } // end namespace anki

+ 8 - 1
src/anki/resource/TextureResource.h

@@ -29,7 +29,7 @@ public:
 	~TextureResource();
 
 	/// Load a texture
-	ANKI_USE_RESULT Error load(const ResourceFilename& filename);
+	ANKI_USE_RESULT Error load(const ResourceFilename& filename, Bool async);
 
 	/// Get the texture
 	const TexturePtr& getGrTexture() const
@@ -68,9 +68,16 @@ public:
 	}
 
 private:
+	static constexpr U MAX_COPIES_BEFORE_FLUSH = 4;
+
+	class TexUploadTask;
+	class LoadingContext;
+
 	TexturePtr m_tex;
 	UVec3 m_size = UVec3(0u);
 	U32 m_layerCount = 0;
+
+	ANKI_USE_RESULT static Error load(LoadingContext& ctx);
 };
 /// @}
 

+ 3 - 2
src/anki/resource/TransferGpuAllocator.h

@@ -100,10 +100,11 @@ public:
 
 	void destroy();
 
-	/// Allocate some transfer memory.
+	/// Allocate some transfer memory. If there is not enough memory it will block until some is releaced. It's
+	/// threadsafe.
 	ANKI_USE_RESULT Error allocate(PtrSize size, TransferGpuAllocatorHandle& handle);
 
-	/// Release the memory. It will not be recycled before the fence is signaled.
+	/// Release the memory. It will not be recycled before the fence is signaled. It's threadsafe.
 	void release(TransferGpuAllocatorHandle& handle, FencePtr fence);
 
 private:

+ 3 - 2
src/anki/script/LuaBinder.cpp

@@ -5,8 +5,7 @@
 
 #include <anki/script/LuaBinder.h>
 #include <anki/util/Logger.h>
-#include <iostream>
-#include <cstring>
+#include <anki/core/Trace.h>
 
 namespace anki
 {
@@ -93,6 +92,8 @@ void* LuaBinder::luaAllocCallback(void* userData, void* ptr, PtrSize osize, PtrS
 
 Error LuaBinder::evalString(const CString& str)
 {
+	ANKI_TRACE_SCOPED_EVENT(LUA_EXEC);
+
 	Error err = ErrorCode::NONE;
 	int e = luaL_dostring(m_l, &str[0]);
 	if(e)

+ 10 - 0
src/anki/util/Functions.h

@@ -233,6 +233,16 @@ constexpr Bool isPacked()
 {
 	return false;
 }
+
+/// Unflatten 3D array index.
+/// Imagine an array [sizeA][sizeB][sizeC] and a flat index in that array. Then this function will compute the unflatten
+/// indices.
+inline void unflatten3dArrayIndex(const U sizeA, const U sizeB, const U sizeC, const U flatIdx, U& a, U& b, U& c)
+{
+	a = (flatIdx / (sizeB * sizeC)) % sizeA;
+	b = (flatIdx / sizeC) % sizeB;
+	c = flatIdx % sizeC;
+}
 /// @}
 
 } // end namespace anki