Переглянути джерело

- Util: Fix HashMap tests
- GR: Some work on the render graph

Panagiotis Christopoulos Charitos 8 роки тому
батько
коміт
d104895e6a

+ 5 - 0
src/anki/gr/Common.h

@@ -150,6 +150,11 @@ public:
 	{
 	}
 
+	Bool operator==(const TextureSurfaceInfo& b) const
+	{
+		return m_level == b.m_level && m_depth == b.m_depth && m_face == b.m_face && m_layer == b.m_layer;
+	}
+
 	U32 m_level = 0;
 	U32 m_depth = 0;
 	U32 m_face = 0;

+ 143 - 16
src/anki/gr/RenderGraph.cpp

@@ -13,6 +13,30 @@
 namespace anki
 {
 
+/// Contains some extra things for render targets.
+class RenderGraph::RT
+{
+public:
+	TextureUsageBit m_usage;
+};
+
+class RenderGraph::RTBarrier
+{
+public:
+	U32 m_rtIdx;
+	TextureUsageBit m_usageBefore;
+	TextureUsageBit m_usageAfter;
+	TextureSurfaceInfo m_surf;
+
+	RTBarrier(U32 rtIdx, TextureUsageBit usageBefore, TextureUsageBit usageAfter, const TextureSurfaceInfo& surf)
+		: m_rtIdx(rtIdx)
+		, m_usageBefore(usageBefore)
+		, m_usageAfter(usageAfter)
+		, m_surf(surf)
+	{
+	}
+};
+
 /// Contains some extra things the RenderPassBase cannot hold.
 class RenderGraph::Pass
 {
@@ -30,9 +54,13 @@ class RenderGraph::Batch
 {
 public:
 	DynamicArrayAuto<U32> m_passes;
+	DynamicArrayAuto<RTBarrier> m_rtBarriersBefore;
+	DynamicArrayAuto<RTBarrier> m_rtBarriersAfter;
 
 	Batch(const StackAllocator<U8>& alloc)
 		: m_passes(alloc)
+		, m_rtBarriersBefore(alloc)
+		, m_rtBarriersAfter(alloc)
 	{
 	}
 };
@@ -40,17 +68,21 @@ public:
 class RenderGraph::BakeContext
 {
 public:
+	StackAllocator<U8> m_alloc;
 	DynamicArrayAuto<Pass> m_passes;
 	BitSet<MAX_RENDER_GRAPH_PASSES> m_passIsInBatch = {false};
 	const RenderGraphDescription* m_descr = nullptr;
 	DynamicArrayAuto<Batch> m_batches;
+	DynamicArrayAuto<RT> m_rts;
 
 	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> m_satisfiedRts = {false}; ///< XXX
 	BitSet<MAX_RENDER_GRAPH_BUFFERS> m_satisfiedConsumerBuffers = {false}; ///< XXX
 
 	BakeContext(const StackAllocator<U8>& alloc)
-		: m_passes(alloc)
+		: m_alloc(alloc)
+		, m_passes(alloc)
 		, m_batches(alloc)
+		, m_rts(alloc)
 	{
 	}
 };
@@ -59,10 +91,6 @@ RenderGraph::RenderGraph(GrManager* manager, U64 hash, GrObjectCache* cache)
 	: GrObject(manager, CLASS_TYPE, hash, cache)
 {
 	ANKI_ASSERT(manager);
-
-	m_tmpAlloc = StackAllocator<U8>(getManager().getAllocator().getMemoryPool().getAllocationCallback(),
-		getManager().getAllocator().getMemoryPool().getAllocationCallbackUserData(),
-		512_B);
 }
 
 RenderGraph::~RenderGraph()
@@ -86,9 +114,10 @@ void RenderGraph::reset()
 Error RenderGraph::dumpDependencyDotFile(const BakeContext& ctx, CString path) const
 {
 	File file;
-	ANKI_CHECK(file.open(StringAuto(m_tmpAlloc).sprintf("%s/rgraph.dot", &path[0]).toCString(), FileOpenFlag::WRITE));
+	ANKI_CHECK(file.open(StringAuto(ctx.m_alloc).sprintf("%s/rgraph.dot", &path[0]).toCString(), FileOpenFlag::WRITE));
 
 	ANKI_CHECK(file.writeText("digraph {\n"));
+	ANKI_CHECK(file.writeText("splines = ortho;\nconcentrate = true;\n"));
 
 	for(U batchIdx = 0; batchIdx < ctx.m_batches.getSize(); ++batchIdx)
 	{
@@ -116,6 +145,29 @@ Error RenderGraph::dumpDependencyDotFile(const BakeContext& ctx, CString path) c
 	}
 
 	ANKI_CHECK(file.writeText("}"));
+
+// Barriers
+#if 0
+	ANKI_CHECK(file.writeText("digraph {\n"));
+	ANKI_CHECK(file.writeText("splines = ortho;\nconcentrate = true;\n"));
+
+	for(U batchIdx = 0; batchIdx < ctx.m_batches.getSize(); ++batchIdx)
+	{
+		const Batch& batch = ctx.m_batches[batchIdx];
+		StringAuto prevBatchName(ctx.m_alloc);
+		if(batchIdx == 0)
+		{
+			prevBatchName.sprintf("%s", "NONE");
+		}
+		else
+		{
+			prevBatchName.sprintf("batch_%u", batchIdx - 1);
+		}
+	}
+
+	ANKI_CHECK(file.writeText("}"));
+#endif
+
 	return Error::NONE;
 }
 
@@ -173,18 +225,26 @@ Bool RenderGraph::passADependsOnB(BakeContext& ctx, const RenderPassBase& a, con
 	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> aWriteBWrite = a.m_producerRtMask & b.m_producerRtMask;
 
 	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> fullDep = aReadBWrite | aWriteBRead | aWriteBWrite;
-	fullDep &= ~ctx.m_satisfiedRts;
+	// fullDep &= ~ctx.m_satisfiedRts;
 
 	if(fullDep.getAny())
 	{
-		// There is overlap
-		for(const RenderPassDependency& dep : a.m_consumers)
+		// There might be an overlap
+
+		for(const RenderPassDependency& consumer : a.m_consumers)
 		{
-			if(dep.m_isTexture && fullDep.get(dep.m_renderTargetHandle))
+			if(consumer.m_isTexture && fullDep.get(consumer.m_texture.m_handle))
 			{
-				depends = true;
-				ANKI_ASSERT(!ctx.m_satisfiedRts.get(dep.m_renderTargetHandle));
-				ctx.m_satisfiedRts.set(dep.m_renderTargetHandle);
+				for(const RenderPassDependency& producer : b.m_producers)
+				{
+					if(producer.m_isTexture && producer.m_texture.m_handle == consumer.m_texture.m_handle
+						&& producer.m_texture.m_surface == consumer.m_texture.m_surface)
+					{
+						depends = true;
+						// ANKI_ASSERT(!ctx.m_satisfiedRts.get(dep.m_texture.m_handle));
+						// ctx.m_satisfiedRts.set(dep.m_texture.m_handle);
+					}
+				}
 			}
 		}
 	}
@@ -244,16 +304,23 @@ void RenderGraph::compileNewGraph(const RenderGraphDescription& descr)
 	ANKI_ASSERT(passCount > 0);
 
 	// Init the context
-	BakeContext ctx(m_tmpAlloc);
+	BakeContext ctx(descr.m_alloc);
 	ctx.m_descr = &descr;
 
+	// Init the render targets
+	ctx.m_rts.create(descr.m_renderTargets.getSize());
+	for(U i = 0; i < descr.m_renderTargets.getSize(); ++i)
+	{
+		ctx.m_rts[i].m_usage = descr.m_renderTargets[i].m_usage;
+	}
+
 	// Find the dependencies between passes
 	for(U i = 0; i < passCount; ++i)
 	{
 		ctx.m_satisfiedRts.unsetAll();
 		ctx.m_satisfiedConsumerBuffers.unsetAll();
 
-		ctx.m_passes.emplaceBack(m_tmpAlloc);
+		ctx.m_passes.emplaceBack(ctx.m_alloc);
 		const RenderPassBase& crntPass = *descr.m_passes[i];
 
 		U j = i;
@@ -271,7 +338,7 @@ void RenderGraph::compileNewGraph(const RenderGraphDescription& descr)
 	U passesInBatchCount = 0;
 	while(passesInBatchCount < passCount)
 	{
-		Batch batch(m_tmpAlloc);
+		Batch batch(ctx.m_alloc);
 
 		for(U i = 0; i < passCount; ++i)
 		{
@@ -293,6 +360,9 @@ void RenderGraph::compileNewGraph(const RenderGraphDescription& descr)
 		}
 	}
 
+	// Create barriers between batches
+	setBatchBarriers(ctx);
+
 #if 1
 	if(dumpDependencyDotFile(ctx, "./"))
 	{
@@ -301,4 +371,61 @@ void RenderGraph::compileNewGraph(const RenderGraphDescription& descr)
 #endif
 }
 
+void RenderGraph::setBatchBarriers(BakeContext& ctx) const
+{
+	// For all batches
+	for(Batch& batch : ctx.m_batches)
+	{
+		// For all passes of that batch
+		for(U passIdx : batch.m_passes)
+		{
+			const RenderPassBase& pass = *ctx.m_descr->m_passes[passIdx];
+
+			// For all consumers
+			for(const RenderPassDependency& consumer : pass.m_consumers)
+			{
+				if(consumer.m_isTexture)
+				{
+					const TextureUsageBit consumerUsage = consumer.m_texture.m_usage;
+					TextureUsageBit& crntUsage = ctx.m_rts[consumer.m_texture.m_handle].m_usage;
+
+					if(crntUsage != consumerUsage)
+					{
+						batch.m_rtBarriersBefore.emplaceBack(
+							consumer.m_texture.m_handle, crntUsage, consumerUsage, consumer.m_texture.m_surface);
+
+						crntUsage = consumerUsage;
+					}
+				}
+				else
+				{
+					// TODO
+				}
+			}
+
+			// For all producers
+			for(const RenderPassDependency& producer : pass.m_producers)
+			{
+				if(producer.m_isTexture)
+				{
+					const TextureUsageBit producerUsage = producer.m_texture.m_usage;
+					TextureUsageBit& crntUsage = ctx.m_rts[producer.m_texture.m_handle].m_usage;
+
+					if(crntUsage != producerUsage)
+					{
+						batch.m_rtBarriersAfter.emplaceBack(
+							producer.m_texture.m_handle, producerUsage, crntUsage, producer.m_texture.m_surface);
+
+						crntUsage = producerUsage;
+					}
+				}
+				else
+				{
+					// TODO
+				}
+			}
+		}
+	}
+}
+
 } // end namespace anki

+ 37 - 20
src/anki/gr/RenderGraph.h

@@ -41,28 +41,38 @@ class RenderPassDependency
 	friend class RenderPassBase;
 
 public:
-	union
+	struct TextureInfo
 	{
-		RenderTargetHandle m_renderTargetHandle;
-		RenderPassBufferHandle m_bufferHandle;
+		RenderTargetHandle m_handle;
+		TextureUsageBit m_usage;
+		TextureSurfaceInfo m_surface;
+	};
+
+	struct BufferInfo
+	{
+		RenderPassBufferHandle m_handle;
+		BufferUsageBit m_usage;
+		PtrSize m_offset;
+		PtrSize m_range;
 	};
 
 	union
 	{
-		TextureUsageBit m_textureUsage;
-		BufferUsageBit m_bufferUsage;
+		TextureInfo m_texture;
+		BufferInfo m_buffer;
 	};
 
-	RenderPassDependency(RenderTargetHandle handle, TextureUsageBit usage)
-		: m_renderTargetHandle(handle)
-		, m_textureUsage(usage)
+	RenderPassDependency(RenderTargetHandle handle,
+		TextureUsageBit usage,
+		const TextureSurfaceInfo& surface = TextureSurfaceInfo(0, 0, 0, 0))
+		: m_texture({handle, usage, surface})
 		, m_isTexture(true)
 	{
 	}
 
-	RenderPassDependency(RenderPassBufferHandle handle, BufferUsageBit usage)
-		: m_bufferHandle(handle)
-		, m_bufferUsage(usage)
+	RenderPassDependency(
+		RenderPassBufferHandle handle, BufferUsageBit usage, PtrSize offset = 0, PtrSize range = MAX_PTR_SIZE)
+		: m_buffer({handle, usage, offset, range})
 		, m_isTexture(false)
 	{
 	}
@@ -111,13 +121,14 @@ public:
 	void newConsumer(const RenderPassDependency& dep)
 	{
 		m_consumers.emplaceBack(m_alloc, dep);
-		if(dep.m_isTexture)
+
+		if(dep.m_isTexture && dep.m_texture.m_usage != TextureUsageBit::NONE)
 		{
-			m_consumerRtMask.set(dep.m_renderTargetHandle);
+			m_consumerRtMask.set(dep.m_texture.m_handle);
 		}
-		else
+		else if(dep.m_buffer.m_usage != BufferUsageBit::NONE)
 		{
-			m_consumerBufferMask.set(dep.m_bufferHandle);
+			m_consumerBufferMask.set(dep.m_buffer.m_handle);
 		}
 	}
 
@@ -126,11 +137,11 @@ public:
 		m_producers.emplaceBack(m_alloc, dep);
 		if(dep.m_isTexture)
 		{
-			m_producerRtMask.set(dep.m_renderTargetHandle);
+			m_producerRtMask.set(dep.m_texture.m_handle);
 		}
 		else
 		{
-			m_producerBufferMask.set(dep.m_bufferHandle);
+			m_producerBufferMask.set(dep.m_buffer.m_handle);
 		}
 	}
 
@@ -216,10 +227,12 @@ private:
 class RenderTarget
 {
 	friend class RenderGraphDescription;
+	friend class RenderGraph;
 
 private:
 	TextureInitInfo m_initInfo;
 	TexturePtr m_importedTex;
+	TextureUsageBit m_usage;
 };
 
 /// XXX
@@ -255,10 +268,11 @@ public:
 	}
 
 	/// XXX
-	U32 importRenderTarget(CString name, TexturePtr tex)
+	U32 importRenderTarget(CString name, TexturePtr tex, TextureUsageBit usage)
 	{
 		RenderTarget rt;
 		rt.m_importedTex = tex;
+		rt.m_usage = usage;
 		m_renderTargets.emplaceBack(m_alloc, rt);
 		return m_renderTargets.getSize() - 1;
 	}
@@ -268,6 +282,7 @@ public:
 	{
 		RenderTarget rt;
 		rt.m_initInfo = initInf;
+		rt.m_usage = TextureUsageBit::NONE;
 		m_renderTargets.emplaceBack(m_alloc, rt);
 		return m_renderTargets.getSize() - 1;
 	}
@@ -334,8 +349,6 @@ public:
 	/// @}
 
 private:
-	StackAllocator<U8> m_tmpAlloc;
-
 	/// Render targets of the same type+size+format.
 	class RenderTargetCacheEntry
 	{
@@ -351,6 +364,8 @@ private:
 	class BakeContext;
 	class Pass;
 	class Batch;
+	class RT;
+	class RTBarrier;
 
 	TexturePtr getOrCreateRenderTarget(const TextureInitInfo& initInf);
 
@@ -359,6 +374,8 @@ private:
 
 	static Bool passADependsOnB(BakeContext& ctx, const RenderPassBase& a, const RenderPassBase& b);
 	static Bool passHasUnmetDependencies(const BakeContext& ctx, U32 passIdx);
+
+	void setBatchBarriers(BakeContext& ctx) const;
 };
 /// @}
 

+ 14 - 1
src/anki/util/HashMap.h

@@ -43,6 +43,7 @@ template<typename TKey, typename TValue, typename THasher = DefaultHasher<TKey>>
 class HashMap
 {
 public:
+	// Typedefs
 	using SparseArrayType = SparseArray<TValue, U64>;
 	using Value = TValue;
 	using Key = TKey;
@@ -50,8 +51,20 @@ public:
 	using Iterator = typename SparseArrayType::Iterator;
 	using ConstIterator = typename SparseArrayType::ConstIterator;
 
+	// Consts
+	/// @see SparseArray::INITIAL_STORAGE_SIZE
+	static constexpr U32 INITIAL_STORAGE_SIZE = SparseArrayType::INITIAL_STORAGE_SIZE;
+	/// @see SparseArray::LINEAR_PROBING_COUNT
+	static constexpr U32 LINEAR_PROBING_COUNT = SparseArrayType::LINEAR_PROBING_COUNT;
+	/// @see SparseArray::MAX_LOAD_FACTOR
+	static constexpr F32 MAX_LOAD_FACTOR = SparseArrayType::MAX_LOAD_FACTOR;
+
 	/// Default constructor.
-	HashMap()
+	/// @copy doc SparseArray::SparseArray
+	HashMap(U32 initialStorageSize = INITIAL_STORAGE_SIZE,
+		U32 probeCount = LINEAR_PROBING_COUNT,
+		F32 maxLoadFactor = MAX_LOAD_FACTOR)
+		: m_sparseArr(initialStorageSize, probeCount, maxLoadFactor)
 	{
 	}
 

+ 12 - 20
src/anki/util/SparseArray.h

@@ -157,8 +157,18 @@ public:
 	using ConstIterator = SparseArrayIterator<const T*, const T&, const SparseArray*>;
 	using Index = TIndex;
 
-	/// Constructor #1.
-	SparseArray(U32 initialStorageSize, U32 probeCount, F32 maxLoadFactor)
+	// Consts
+	static constexpr U32 INITIAL_STORAGE_SIZE = 64; ///< The initial storage size of the array.
+	static constexpr U32 LINEAR_PROBING_COUNT = 8; ///< The number of linear probes.
+	static constexpr F32 MAX_LOAD_FACTOR = 0.8f; ///< Load factor.
+
+	/// Constructor.
+	/// @param initialStorageSize The initial size of the array.
+	/// @param probeCount         The number of probe queries. It's the linear probe count the sparse array is using.
+	/// @param maxLoadFactor      If storage is loaded more than maxLoadFactor then increase it.
+	SparseArray(U32 initialStorageSize = INITIAL_STORAGE_SIZE,
+		U32 probeCount = LINEAR_PROBING_COUNT,
+		F32 maxLoadFactor = MAX_LOAD_FACTOR)
 		: m_initialStorageSize(initialStorageSize)
 		, m_probeCount(probeCount)
 		, m_maxLoadFactor(maxLoadFactor)
@@ -168,24 +178,6 @@ public:
 		ANKI_ASSERT(maxLoadFactor > 0.5f && maxLoadFactor < 1.0f);
 	}
 
-	/// Constructor #2.
-	SparseArray(U32 initialStorageSize, U32 probeCount)
-		: SparseArray(initialStorageSize, probeCount, 0.8f)
-	{
-	}
-
-	/// Constructor #3.
-	SparseArray(U32 initialStorageSize)
-		: SparseArray(initialStorageSize, log2(initialStorageSize))
-	{
-	}
-
-	/// Constructor #4.
-	SparseArray()
-		: SparseArray(64)
-	{
-	}
-
 	/// Non-copyable.
 	SparseArray(const SparseArray&) = delete;
 

+ 23 - 0
src/anki/util/String.h

@@ -616,6 +616,17 @@ public:
 	{
 	}
 
+	/// Copy construcor.
+	StringAuto(const StringAuto& b)
+		: Base()
+		, m_alloc(b.m_alloc)
+	{
+		if(!b.isEmpty())
+		{
+			create(b.m_data.getBegin(), b.m_data.getEnd());
+		}
+	}
+
 	/// Move constructor.
 	StringAuto(StringAuto&& b)
 		: Base()
@@ -667,6 +678,18 @@ public:
 		Base::destroy(m_alloc);
 	}
 
+	/// Copy operator.
+	StringAuto& operator=(const StringAuto& b)
+	{
+		destroy();
+		m_alloc = b.m_alloc;
+		if(!b.isEmpty())
+		{
+			create(b.m_data.getBegin(), b.m_data.getEnd());
+		}
+		return *this;
+	}
+
 	/// Move one string to this one.
 	StringAuto& operator=(StringAuto&& b)
 	{

+ 30 - 18
tests/gr/Gr.cpp

@@ -1539,6 +1539,8 @@ ANKI_TEST(Gr, RenderGraph)
 	RenderGraphDescription descr(alloc);
 	RenderGraphPtr rgraph = gr->newInstance<RenderGraph>();
 
+	const U GI_MIP_COUNT = 4;
+
 	TextureInitInfo texInf("sm_scratch");
 	texInf.m_width = texInf.m_height = 16;
 	texInf.m_usage = TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE | TextureUsageBit::SAMPLED_FRAGMENT;
@@ -1561,21 +1563,6 @@ ANKI_TEST(Gr, RenderGraph)
 		pass.newProducer({smExpRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
 	}
 
-	// SM 2nd batch
-	{
-		GraphicsRenderPassInfo& pass = descr.newGraphicsRenderPass("SM #2");
-		pass.newConsumer({smScratchRt, TextureUsageBit::SAMPLED_FRAGMENT});
-		pass.newProducer({smScratchRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE});
-	}
-
-	// SM to exponential SM 2nd batch
-	{
-		GraphicsRenderPassInfo& pass = descr.newGraphicsRenderPass("ESM #2");
-		pass.newConsumer({smScratchRt, TextureUsageBit::SAMPLED_FRAGMENT});
-		pass.newConsumer({smExpRt, TextureUsageBit::SAMPLED_FRAGMENT});
-		pass.newProducer({smExpRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
-	}
-
 	// GI gbuff
 	RenderTargetHandle giGbuffNormRt = descr.newRenderTarget("GI gbuff norm", texInf);
 	RenderTargetHandle giGbuffDiffRt = descr.newRenderTarget("GI gbuff diff", texInf);
@@ -1593,16 +1580,40 @@ ANKI_TEST(Gr, RenderGraph)
 
 	// GI light
 	RenderTargetHandle giGiLightRt = descr.newRenderTarget("GI light", texInf);
+	for(U faceIdx = 0; faceIdx < 6; ++faceIdx)
 	{
-		GraphicsRenderPassInfo& pass = descr.newGraphicsRenderPass("GI light");
-		pass.newConsumer({giGiLightRt, TextureUsageBit::NONE});
+		GraphicsRenderPassInfo& pass =
+			descr.newGraphicsRenderPass(StringAuto(alloc).sprintf("GI li%u", faceIdx).toCString());
+		pass.newConsumer({giGiLightRt, TextureUsageBit::NONE, TextureSurfaceInfo(0, 0, faceIdx, 0)});
 		pass.newConsumer({giGbuffNormRt, TextureUsageBit::SAMPLED_FRAGMENT});
 		pass.newConsumer({giGbuffDepthRt, TextureUsageBit::SAMPLED_FRAGMENT});
 		pass.newConsumer({giGbuffDiffRt, TextureUsageBit::SAMPLED_FRAGMENT});
 
-		pass.newProducer({giGiLightRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newProducer(
+			{giGiLightRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, TextureSurfaceInfo(0, 0, faceIdx, 0)});
 	}
 
+	// GI light mips
+	{
+		for(U faceIdx = 0; faceIdx < 6; ++faceIdx)
+		{
+			GraphicsRenderPassInfo& pass =
+				descr.newGraphicsRenderPass(StringAuto(alloc).sprintf("GI mip%u", faceIdx).toCString());
+
+			pass.newConsumer(
+				{giGiLightRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE, TextureSurfaceInfo(0, 0, faceIdx, 0)});
+			pass.newConsumer({giGiLightRt, TextureUsageBit::GENERATE_MIPMAPS, TextureSurfaceInfo(0, 0, faceIdx, 0)});
+
+			for(U mip = 1; mip < GI_MIP_COUNT; ++mip)
+			{
+				TextureSurfaceInfo surf(mip, 0, faceIdx, 0);
+				pass.newConsumer({giGiLightRt, TextureUsageBit::NONE, surf});
+				pass.newProducer({giGiLightRt, TextureUsageBit::GENERATE_MIPMAPS, surf});
+			}
+		}
+	}
+
+#if 0
 	// Gbuffer
 	RenderTargetHandle gbuffRt0 = descr.newRenderTarget("Gbuff rt0", texInf);
 	RenderTargetHandle gbuffRt1 = descr.newRenderTarget("Gbuff rt1", texInf);
@@ -1636,6 +1647,7 @@ ANKI_TEST(Gr, RenderGraph)
 
 		pass.newProducer({giGiLightRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
 	}
+#endif
 
 	rgraph->compileNewGraph(descr);
 	COMMON_END()

+ 68 - 71
tests/util/HashMap.cpp

@@ -143,7 +143,7 @@ ANKI_TEST(Util, HashMap)
 	// Bench it
 	{
 		using AkMap = HashMap<int, int, Hasher>;
-		AkMap akMap;
+		AkMap akMap(128, 32, 0.9f);
 		using StlMap =
 			std::unordered_map<int, int, std::hash<int>, std::equal_to<int>, HeapAllocator<std::pair<int, int>>>;
 		StlMap stdMap(10, std::hash<int>(), std::equal_to<int>(), alloc);
@@ -151,10 +151,9 @@ ANKI_TEST(Util, HashMap)
 		std::unordered_map<int, int> tmpMap;
 
 		HighRezTimer timer;
-		I64 count = 0;
 
 		// Create a huge set
-		const U COUNT = 1024 * 1024;
+		const U COUNT = 1024 * 1024 * 10;
 		DynamicArrayAuto<int> vals(alloc);
 		vals.create(COUNT);
 
@@ -171,90 +170,88 @@ ANKI_TEST(Util, HashMap)
 			vals[i] = v;
 		}
 
-		// Put the vals AnKi
-		timer.start();
-		for(U i = 0; i < COUNT; ++i)
-		{
-			akMap.emplace(alloc, vals[i], vals[i]);
-		}
-		timer.stop();
-		Second akTime = timer.getElapsedTime();
-
-		// Put the vals STL
-		timer.start();
-		for(U i = 0; i < COUNT; ++i)
+		// Insertion
 		{
-			stdMap[vals[i]] = vals[i];
-		}
-		timer.stop();
-		Second stlTime = timer.getElapsedTime();
+			// Put the vals AnKi
+			timer.start();
+			for(U i = 0; i < COUNT; ++i)
+			{
+				akMap.emplace(alloc, vals[i], vals[i]);
+			}
+			timer.stop();
+			Second akTime = timer.getElapsedTime();
 
-		printf("Inserting bench: STL %f AnKi %f | %f%%\n", stlTime, akTime, stlTime / akTime * 100.0);
+			// Put the vals STL
+			timer.start();
+			for(U i = 0; i < COUNT; ++i)
+			{
+				stdMap[vals[i]] = vals[i];
+			}
+			timer.stop();
+			Second stlTime = timer.getElapsedTime();
 
-		// Find values AnKi
-		timer.start();
-		for(U i = 0; i < COUNT; ++i)
-		{
-			auto it = akMap.find(vals[i]);
-			count += *it;
+			ANKI_TEST_LOGI("Inserting bench: STL %f AnKi %f | %f%%", stlTime, akTime, stlTime / akTime * 100.0);
 		}
-		timer.stop();
-		akTime = timer.getElapsedTime();
 
-		// Find values STL
-		timer.start();
-		for(U i = 0; i < COUNT; ++i)
+		// Search
 		{
-			count += stdMap[vals[i]];
-		}
-		timer.stop();
-		stlTime = timer.getElapsedTime();
+			I64 count = 0; // To avoid compiler opts
 
-		printf("Find bench: STL %f AnKi %f | %f%%\n", stlTime, akTime, stlTime / akTime * 100.0);
+			// Find values AnKi
+			timer.start();
+			for(U i = 0; i < COUNT; ++i)
+			{
+				auto it = akMap.find(vals[i]);
+				count += *it;
+			}
+			timer.stop();
+			Second akTime = timer.getElapsedTime();
 
-		// Create a random set for deletion out of vals
-		tmpMap.clear();
-		const U DEL_COUNT = COUNT / 2;
-		DynamicArrayAuto<AkMap::Iterator> delValsAk(alloc);
-		delValsAk.create(DEL_COUNT);
+			// Find values STL
+			timer.start();
+			for(U i = 0; i < COUNT; ++i)
+			{
+				count += stdMap[vals[i]];
+			}
+			timer.stop();
+			Second stlTime = timer.getElapsedTime();
 
-		DynamicArrayAuto<StlMap::iterator> delValsStl(alloc);
-		delValsStl.create(DEL_COUNT);
+			ANKI_TEST_LOGI(
+				"Find bench: STL %f AnKi %f | %f%% (%lld)", stlTime, akTime, stlTime / akTime * 100.0, count);
+		}
 
-		for(U i = 0; i < DEL_COUNT; ++i)
+		// Delete
 		{
-			// Put unique keys
-			int v;
-			do
+			// Remove in random order
+			std::random_shuffle(vals.begin(), vals.end());
+
+			// Random delete AnKi
+			Second akTime = 0.0;
+			for(U i = 0; i < vals.getSize(); ++i)
 			{
-				v = vals[rand() % COUNT];
-			} while(tmpMap.find(v) != tmpMap.end());
-			tmpMap[v] = 1;
+				auto it = akMap.find(vals[i]);
 
-			delValsAk[i] = akMap.find(v);
-			delValsStl[i] = stdMap.find(v);
-		}
+				timer.start();
+				akMap.erase(alloc, it);
+				timer.stop();
+				akTime += timer.getElapsedTime();
+			}
 
-		// Random delete AnKi
-		timer.start();
-		for(U i = 0; i < DEL_COUNT; ++i)
-		{
-			akMap.erase(alloc, delValsAk[i]);
-		}
-		timer.stop();
-		akTime = timer.getElapsedTime();
+			// Random delete STL
+			Second stlTime = 0.0;
+			for(U i = 0; i < vals.getSize(); ++i)
+			{
+				auto it = stdMap.find(vals[i]);
 
-		// Random delete STL
-		timer.start();
-		for(U i = 0; i < DEL_COUNT; ++i)
-		{
-			stdMap.erase(delValsStl[i]);
-		}
-		timer.stop();
-		stlTime = timer.getElapsedTime();
+				timer.start();
+				stdMap.erase(it);
+				timer.stop();
+				stlTime += timer.getElapsedTime();
+			}
 
-		printf("Deleting bench: STL %f AnKi %f | %f%%\n", stlTime, akTime, stlTime / akTime * 100.0);
+			ANKI_TEST_LOGI("Deleting bench: STL %f AnKi %f | %f%%", stlTime, akTime, stlTime / akTime * 100.0);
+		}
 
 		akMap.destroy(alloc);
 	}
-}
+}