Răsfoiți Sursa

Gr: Some work on the render graph

Panagiotis Christopoulos Charitos 8 ani în urmă
părinte
comite
2508261cac
5 a modificat fișierele cu 303 adăugiri și 205 ștergeri
  1. 154 105
      src/anki/gr/RenderGraph.cpp
  2. 66 90
      src/anki/gr/RenderGraph.h
  3. 2 1
      src/anki/util/DynamicArray.h
  4. 7 0
      src/anki/util/String.h
  5. 74 9
      tests/gr/Gr.cpp

+ 154 - 105
src/anki/gr/RenderGraph.cpp

@@ -27,6 +27,13 @@ public:
 	DynamicArray<Usage> m_surfUsages;
 };
 
+/// Same as RT but for buffers.
+class RenderGraph::Buffer
+{
+public:
+	BufferUsageBit m_usage;
+};
+
 /// Pipeline barrier of texture or buffer.
 class RenderGraph::Barrier
 {
@@ -71,46 +78,30 @@ public:
 class RenderGraph::Pass
 {
 public:
-	DynamicArrayAuto<U32> m_dependsOn;
-
-	Pass(const StackAllocator<U8>& alloc)
-		: m_dependsOn(alloc)
-	{
-	}
+	DynamicArray<U32> m_dependsOn;
 };
 
 /// A batch of render passes. These passes can run in parallel.
 class RenderGraph::Batch
 {
 public:
-	DynamicArrayAuto<U32> m_passes;
-	DynamicArrayAuto<Barrier> m_barriersBefore;
-
-	Batch(const StackAllocator<U8>& alloc)
-		: m_passes(alloc)
-		, m_barriersBefore(alloc)
-	{
-	}
+	DynamicArray<U32> m_passes;
+	DynamicArray<Barrier> m_barriersBefore;
 };
 
 class RenderGraph::BakeContext
 {
 public:
 	StackAllocator<U8> m_alloc;
-	DynamicArrayAuto<Pass> m_passes;
+	DynamicArray<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
+	DynamicArray<Batch> m_batches;
+	DynamicArray<RT> m_rts;
+	DynamicArray<Buffer> m_buffers;
 
 	BakeContext(const StackAllocator<U8>& alloc)
 		: m_alloc(alloc)
-		, m_passes(alloc)
-		, m_batches(alloc)
-		, m_rts(alloc)
 	{
 	}
 };
@@ -185,50 +176,63 @@ TexturePtr RenderGraph::getOrCreateRenderTarget(const TextureInitInfo& initInf)
 
 Bool RenderGraph::passADependsOnB(BakeContext& ctx, const RenderPassBase& a, const RenderPassBase& b)
 {
-	/// Compute the 3 types of dependencies
-	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> aReadBWrite = a.m_consumerRtMask & b.m_producerRtMask;
-	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> aWriteBRead = a.m_producerRtMask & b.m_consumerRtMask;
-	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> aWriteBWrite = a.m_producerRtMask & b.m_producerRtMask;
-
-	BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> fullDep = aReadBWrite | aWriteBRead | aWriteBWrite;
-
-	if(fullDep.getAny())
+	// Render targets
 	{
-		// There might be an overlap
+		/// Compute the 3 types of dependencies
+		BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> aReadBWrite = a.m_consumerRtMask & b.m_producerRtMask;
+		BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> aWriteBRead = a.m_producerRtMask & b.m_consumerRtMask;
+		BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> aWriteBWrite = a.m_producerRtMask & b.m_producerRtMask;
+
+		BitSet<MAX_RENDER_GRAPH_RENDER_TARGETS> fullDep = aReadBWrite | aWriteBRead | aWriteBWrite;
 
-		for(const RenderPassDependency& consumer : a.m_consumers)
+		if(fullDep.getAny())
 		{
-			if(consumer.m_isTexture && fullDep.get(consumer.m_texture.m_handle))
+			// There might be an overlap
+
+			for(const RenderPassDependency& consumer : a.m_consumers)
 			{
-				for(const RenderPassDependency& producer : b.m_producers)
+				if(consumer.m_isTexture && fullDep.get(consumer.m_texture.m_handle))
 				{
-					if(producer.m_isTexture && producer.m_texture.m_handle == consumer.m_texture.m_handle
-						&& producer.m_texture.m_surface == consumer.m_texture.m_surface)
+					for(const RenderPassDependency& producer : b.m_producers)
 					{
-						return true;
+						if(producer.m_isTexture && producer.m_texture.m_handle == consumer.m_texture.m_handle
+							&& producer.m_texture.m_surface == consumer.m_texture.m_surface)
+						{
+							return true;
+						}
 					}
 				}
 			}
 		}
 	}
 
-#if 0
-	BitSet<MAX_RENDER_GRAPH_BUFFERS> bufferIntersection =
-		(a.m_consumerBufferMask & ~ctx.m_satisfiedConsumerBuffers) & b.m_producerBufferMask;
-	if(bufferIntersection.getAny())
+	// Buffers
 	{
-		// There is overlap
-		for(const RenderPassDependency& dep : a.m_consumers)
+		BitSet<MAX_RENDER_GRAPH_BUFFERS> aReadBWrite = a.m_consumerBufferMask & b.m_producerBufferMask;
+		BitSet<MAX_RENDER_GRAPH_BUFFERS> aWriteBRead = a.m_producerBufferMask & b.m_consumerBufferMask;
+		BitSet<MAX_RENDER_GRAPH_BUFFERS> aWriteBWrite = a.m_producerBufferMask & b.m_producerBufferMask;
+
+		BitSet<MAX_RENDER_GRAPH_BUFFERS> fullDep = aReadBWrite | aWriteBRead | aWriteBWrite;
+
+		if(fullDep.getAny())
 		{
-			if(!dep.m_isTexture && bufferIntersection.get(dep.m_bufferHandle))
+			// There might be an overlap
+
+			for(const RenderPassDependency& consumer : a.m_consumers)
 			{
-				depends = true;
-				ANKI_ASSERT(!ctx.m_satisfiedConsumerBuffers.get(dep.m_bufferHandle));
-				ctx.m_satisfiedConsumerBuffers.set(dep.m_bufferHandle);
+				if(!consumer.m_isTexture && fullDep.get(consumer.m_buffer.m_handle))
+				{
+					for(const RenderPassDependency& producer : b.m_producers)
+					{
+						if(!producer.m_isTexture && producer.m_buffer.m_handle == consumer.m_buffer.m_handle)
+						{
+							return true;
+						}
+					}
+				}
 			}
 		}
 	}
-#endif
 
 	return false;
 }
@@ -267,19 +271,23 @@ void RenderGraph::compileNewGraph(const RenderGraphDescription& descr)
 	ANKI_ASSERT(passCount > 0);
 
 	// Init the context
-	BakeContext ctx(descr.m_alloc);
+	auto alloc = descr.m_alloc;
+	BakeContext& ctx = *alloc.newInstance<BakeContext>(descr.m_alloc);
+	m_ctx = &ctx;
 	ctx.m_descr = &descr;
 
-	// Init the render targets
-	ctx.m_rts.create(descr.m_renderTargets.getSize());
+	// Init the resources
+	ctx.m_rts.create(alloc, descr.m_renderTargets.getSize());
+	ctx.m_buffers.create(alloc, descr.m_buffers.getSize());
+	for(U buffIdx = 0; buffIdx < ctx.m_buffers.getSize(); ++buffIdx)
+	{
+		ctx.m_buffers[buffIdx].m_usage = descr.m_buffers[buffIdx].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(ctx.m_alloc);
+		ctx.m_passes.emplaceBack(alloc);
 		const RenderPassBase& crntPass = *descr.m_passes[i];
 
 		U j = i;
@@ -288,7 +296,7 @@ void RenderGraph::compileNewGraph(const RenderGraphDescription& descr)
 			const RenderPassBase& prevPass = *descr.m_passes[j];
 			if(passADependsOnB(ctx, crntPass, prevPass))
 			{
-				ctx.m_passes[i].m_dependsOn.emplaceBack(j);
+				ctx.m_passes[i].m_dependsOn.emplaceBack(alloc, j);
 			}
 		}
 	}
@@ -297,7 +305,7 @@ void RenderGraph::compileNewGraph(const RenderGraphDescription& descr)
 	U passesInBatchCount = 0;
 	while(passesInBatchCount < passCount)
 	{
-		Batch batch(ctx.m_alloc);
+		Batch batch;
 
 		for(U i = 0; i < passCount; ++i)
 		{
@@ -305,12 +313,12 @@ void RenderGraph::compileNewGraph(const RenderGraphDescription& descr)
 			{
 				// Add to the batch
 				++passesInBatchCount;
-				batch.m_passes.emplaceBack(i);
+				batch.m_passes.emplaceBack(alloc, i);
 			}
 		}
 
 		// Push back batch
-		ctx.m_batches.emplaceBack(std::move(batch));
+		ctx.m_batches.emplaceBack(alloc, std::move(batch));
 
 		// Mark batch's passes done
 		for(U32 passIdx : ctx.m_batches.getBack().m_passes)
@@ -328,13 +336,6 @@ void RenderGraph::compileNewGraph(const RenderGraphDescription& descr)
 		ANKI_LOGF("Won't recover on debug code");
 	}
 #endif
-
-#if ANKI_EXTRA_CHECKS
-	for(RT& rt : ctx.m_rts)
-	{
-		rt.m_surfUsages.destroy(ctx.m_alloc);
-	}
-#endif
 }
 
 void RenderGraph::setBatchBarriers(BakeContext& ctx) const
@@ -363,22 +364,21 @@ void RenderGraph::setBatchBarriers(BakeContext& ctx) const
 					{
 						if(wholeTex || u.m_surf == consumer.m_texture.m_surface)
 						{
+							anySurfaceFound = true;
 							if(u.m_usage != consumerUsage)
 							{
-								batch.m_barriersBefore.emplaceBack(consumer.m_texture.m_handle,
-									u.m_usage,
-									consumerUsage,
-									consumer.m_texture.m_surface);
+								batch.m_barriersBefore.emplaceBack(
+									alloc, consumer.m_texture.m_handle, u.m_usage, consumerUsage, u.m_surf);
 
 								u.m_usage = consumer.m_texture.m_usage;
-								anySurfaceFound = true;
 							}
 						}
 					}
 
 					if(!anySurfaceFound && ctx.m_descr->m_renderTargets[rtIdx].m_usage != consumerUsage)
 					{
-						batch.m_barriersBefore.emplaceBack(consumer.m_texture.m_handle,
+						batch.m_barriersBefore.emplaceBack(alloc,
+							rtIdx,
 							ctx.m_descr->m_renderTargets[rtIdx].m_usage,
 							consumerUsage,
 							consumer.m_texture.m_surface);
@@ -389,16 +389,25 @@ void RenderGraph::setBatchBarriers(BakeContext& ctx) const
 				}
 				else
 				{
-					// TODO
+					const U32 buffIdx = consumer.m_buffer.m_handle;
+					const BufferUsageBit consumerUsage = consumer.m_buffer.m_usage;
+
+					if(consumerUsage != ctx.m_buffers[buffIdx].m_usage)
+					{
+						batch.m_barriersBefore.emplaceBack(
+							alloc, buffIdx, ctx.m_buffers[buffIdx].m_usage, consumerUsage);
+
+						ctx.m_buffers[buffIdx].m_usage = consumerUsage;
+					}
 				}
 			}
 		}
 	}
 }
 
-StringAuto RenderGraph::textureUsageToStr(const BakeContext& ctx, TextureUsageBit usage)
+StringAuto RenderGraph::textureUsageToStr(StackAllocator<U8>& alloc, TextureUsageBit usage)
 {
-	StringListAuto slist(ctx.m_alloc);
+	StringListAuto slist(alloc);
 
 #define ANKI_TEX_USAGE(u)                \
 	if(!!(usage & TextureUsageBit::u))   \
@@ -426,7 +435,57 @@ StringAuto RenderGraph::textureUsageToStr(const BakeContext& ctx, TextureUsageBi
 
 #undef ANKI_TEX_USAGE
 
-	StringAuto str(ctx.m_alloc);
+	StringAuto str(alloc);
+	slist.join(" | ", str);
+	return str;
+}
+
+StringAuto RenderGraph::bufferUsageToStr(StackAllocator<U8>& alloc, BufferUsageBit usage)
+{
+	StringListAuto slist(alloc);
+
+#define ANKI_BUFF_USAGE(u)               \
+	if(!!(usage & BufferUsageBit::u))    \
+	{                                    \
+		slist.pushBackSprintf("%s", #u); \
+	}
+
+	ANKI_BUFF_USAGE(UNIFORM_VERTEX);
+	ANKI_BUFF_USAGE(UNIFORM_TESSELLATION_EVALUATION);
+	ANKI_BUFF_USAGE(UNIFORM_TESSELLATION_CONTROL);
+	ANKI_BUFF_USAGE(UNIFORM_GEOMETRY);
+	ANKI_BUFF_USAGE(UNIFORM_FRAGMENT);
+	ANKI_BUFF_USAGE(UNIFORM_COMPUTE);
+	ANKI_BUFF_USAGE(STORAGE_VERTEX_READ);
+	ANKI_BUFF_USAGE(STORAGE_VERTEX_WRITE);
+	ANKI_BUFF_USAGE(STORAGE_TESSELLATION_EVALUATION_READ);
+	ANKI_BUFF_USAGE(STORAGE_TESSELLATION_EVALUATION_WRITE);
+	ANKI_BUFF_USAGE(STORAGE_TESSELLATION_CONTROL_READ);
+	ANKI_BUFF_USAGE(STORAGE_TESSELLATION_CONTROL_WRITE);
+	ANKI_BUFF_USAGE(STORAGE_GEOMETRY_READ);
+	ANKI_BUFF_USAGE(STORAGE_GEOMETRY_WRITE);
+	ANKI_BUFF_USAGE(STORAGE_FRAGMENT_READ);
+	ANKI_BUFF_USAGE(STORAGE_FRAGMENT_WRITE);
+	ANKI_BUFF_USAGE(STORAGE_COMPUTE_READ);
+	ANKI_BUFF_USAGE(STORAGE_COMPUTE_WRITE);
+	ANKI_BUFF_USAGE(TEXTURE_VERTEX_READ);
+	ANKI_BUFF_USAGE(TEXTURE_TESSELLATION_EVALUATION_READ);
+	ANKI_BUFF_USAGE(TEXTURE_TESSELLATION_CONTROL_READ);
+	ANKI_BUFF_USAGE(TEXTURE_GEOMETRY_READ);
+	ANKI_BUFF_USAGE(TEXTURE_FRAGMENT_READ);
+	ANKI_BUFF_USAGE(TEXTURE_COMPUTE_READ);
+	ANKI_BUFF_USAGE(INDEX);
+	ANKI_BUFF_USAGE(VERTEX);
+	ANKI_BUFF_USAGE(INDIRECT);
+	ANKI_BUFF_USAGE(FILL);
+	ANKI_BUFF_USAGE(BUFFER_UPLOAD_SOURCE);
+	ANKI_BUFF_USAGE(BUFFER_UPLOAD_DESTINATION);
+	ANKI_BUFF_USAGE(TEXTURE_UPLOAD_SOURCE);
+	ANKI_BUFF_USAGE(QUERY_RESULT);
+
+#undef ANKI_BUFF_USAGE
+
+	StringAuto str(alloc);
 	slist.join(" | ", str);
 	return str;
 }
@@ -503,45 +562,35 @@ Error RenderGraph::dumpDependencyDotFile(const BakeContext& ctx, CString path) c
 					barrier.m_tex.m_surf.m_depth,
 					barrier.m_tex.m_surf.m_face,
 					barrier.m_tex.m_surf.m_layer,
-					&textureUsageToStr(ctx, barrier.m_tex.m_usageBefore).toCString()[0],
-					&textureUsageToStr(ctx, barrier.m_tex.m_usageAfter).toCString()[0]);
+					&textureUsageToStr(alloc, barrier.m_tex.m_usageBefore).toCString()[0],
+					&textureUsageToStr(alloc, barrier.m_tex.m_usageAfter).toCString()[0]);
 			}
 			else
 			{
-				// TODO
+				barrierName.sprintf("%s barrier%u\n%s\n%s -> %s",
+					&batchName[0],
+					barrierIdx,
+					&ctx.m_descr->m_buffers[barrier.m_buff.m_idx].m_name[0],
+					&bufferUsageToStr(alloc, barrier.m_buff.m_usageBefore).toCString()[0],
+					&bufferUsageToStr(alloc, barrier.m_buff.m_usageAfter).toCString()[0]);
 			}
 
 			slist.pushBackSprintf("\t\"%s\"[color=%s,style=bold,shape=box];\n", &barrierName[0], COLORS[batchIdx % 6]);
 			slist.pushBackSprintf("\t\"%s\"->\"%s\";\n", &prevBubble[0], &barrierName[0]);
 
-#if 0
-			// Print dep
-			if(barrier.m_isTexture)
-			{
-				StringAuto rtName(ctx.m_alloc);
-				rtName.sprintf("%s", &ctx.m_descr->m_renderTargets[barrier.m_tex.m_idx].m_name[0]);
-
-				slist.pushBackSprintf("\t\"%s\":w->\"%s\":e[label=\"(mip,dp,f,l)=(%u,%u,%u,%u)\";color=%s];\n",
-					&rtName[0],
-					&barrierName[0],
-					barrier.m_tex.m_surf.m_level,
-					barrier.m_tex.m_surf.m_depth,
-					barrier.m_tex.m_surf.m_face,
-					barrier.m_tex.m_surf.m_layer,
-					COLORS[barrier.m_tex.m_idx % 6]);
-			}
-			else
-			{
-				// TODO
-			}
-#endif
-
 			prevBubble = barrierName;
 		}
 
-		slist.pushBackSprintf("\t\"%s\"[color=%s,style=bold];\n", &batchName[0], COLORS[batchIdx % 6]);
-		slist.pushBackSprintf("\t\"%s\"->\"%s\";\n", &prevBubble[0], &batchName[0]);
-		prevBubble = batchName;
+		for(U passIdx : batch.m_passes)
+		{
+			const RenderPassBase& pass = *ctx.m_descr->m_passes[passIdx];
+			StringAuto passName(alloc);
+			passName.sprintf("_%s_", pass.m_name.cstr());
+			slist.pushBackSprintf("\t\"pass: %s\"[color=%s,style=bold];\n", passName.cstr(), COLORS[batchIdx % 6]);
+			slist.pushBackSprintf("\t\"%s\"->\"pass: %s\";\n", prevBubble.cstr(), passName.cstr());
+
+			prevBubble = passName;
+		}
 	}
 	slist.pushBackSprintf("}\n");
 

+ 66 - 90
src/anki/gr/RenderGraph.h

@@ -8,6 +8,7 @@
 #include <anki/gr/GrObject.h>
 #include <anki/gr/Enums.h>
 #include <anki/gr/Texture.h>
+#include <anki/gr/Buffer.h>
 #include <anki/util/HashMap.h>
 #include <anki/util/BitSet.h>
 
@@ -20,21 +21,21 @@ namespace anki
 /// @name RenderGraph constants
 /// @{
 static constexpr U MAX_RENDER_GRAPH_PASSES = 128;
-static constexpr U MAX_RENDER_GRAPH_RENDER_TARGETS = 128;
+static constexpr U MAX_RENDER_GRAPH_RENDER_TARGETS = 128; ///< Max imported or not render targets in RenderGraph.
 static constexpr U MAX_RENDER_GRAPH_BUFFERS = 64;
 /// @}
 
-/// XXX
+/// Render target handle used in the RenderGraph.
 using RenderTargetHandle = U32;
 
-/// XXX
+/// Buffer handle used in the RenderGraph.
 using RenderPassBufferHandle = U32;
 
-/// XXX
+/// Work callback for a RenderGraph pass.
 using RenderPassWorkCallback = void (*)(
 	void* userData, CommandBufferPtr cmdb, U32 secondLevelCmdbIdx, U32 secondLevelCmdbCount);
 
-/// XXX
+/// RenderGraph pass dependency.
 class RenderPassDependency
 {
 	friend class RenderGraph;
@@ -55,8 +56,8 @@ public:
 	{
 	}
 
-	RenderPassDependency(RenderPassBufferHandle handle, BufferUsageBit usage, PtrSize offset, PtrSize range)
-		: m_buffer({handle, usage, offset, range})
+	RenderPassDependency(RenderPassBufferHandle handle, BufferUsageBit usage)
+		: m_buffer({handle, usage})
 		, m_isTexture(false)
 	{
 	}
@@ -74,8 +75,6 @@ private:
 	{
 		RenderPassBufferHandle m_handle;
 		BufferUsageBit m_usage;
-		PtrSize m_offset;
-		PtrSize m_range;
 	};
 
 	union
@@ -87,24 +86,7 @@ private:
 	Bool8 m_isTexture;
 };
 
-/// Describes how the render target will be used inside a graphics render pass.
-class RenderTargetReference
-{
-public:
-	RenderTargetHandle m_renderTargetHandle;
-
-	TextureSurfaceInfo m_surface;
-	AttachmentLoadOperation m_loadOperation = AttachmentLoadOperation::CLEAR;
-	AttachmentStoreOperation m_storeOperation = AttachmentStoreOperation::STORE;
-	ClearValue m_clearValue;
-
-	AttachmentLoadOperation m_stencilLoadOperation = AttachmentLoadOperation::CLEAR;
-	AttachmentStoreOperation m_stencilStoreOperation = AttachmentStoreOperation::STORE;
-
-	DepthStencilAspectBit m_aspect = DepthStencilAspectBit::NONE; ///< Relevant only for depth stencil textures.
-};
-
-/// The base of compute/transfer and graphics renderpasses
+/// The base of compute/transfer and graphics renderpasses for RenderGraph.
 class RenderPassBase
 {
 	friend class RenderGraph;
@@ -124,6 +106,7 @@ public:
 		m_secondLevelCmdbsCount = secondLeveCmdbCount;
 	}
 
+	/// Add new consumer dependency.
 	void newConsumer(const RenderPassDependency& dep)
 	{
 		m_consumers.emplaceBack(m_alloc, dep);
@@ -138,6 +121,7 @@ public:
 		}
 	}
 
+	/// Add new producer dependency.
 	void newProducer(const RenderPassDependency& dep)
 	{
 		m_producers.emplaceBack(m_alloc, dep);
@@ -187,7 +171,7 @@ protected:
 	}
 };
 
-/// XXX
+/// A graphics render pass for RenderGraph.
 class GraphicsRenderPassInfo : public RenderPassBase
 {
 	friend class RenderGraphDescription;
@@ -197,29 +181,9 @@ public:
 		: RenderPassBase(Type::GRAPHICS)
 	{
 	}
-
-	~GraphicsRenderPassInfo()
-	{
-	}
-
-	void setColorRenderTarget(U32 location, const RenderTargetReference& ref)
-	{
-		m_colorAttachments[location] = ref;
-		m_colorAttachmentCount = location + 1;
-	}
-
-	void setDepthStencilRenderTarget(const RenderTargetReference& ref)
-	{
-		m_depthStencilAttachment = ref;
-	}
-
-private:
-	Array<RenderTargetReference, MAX_COLOR_ATTACHMENTS> m_colorAttachments;
-	U32 m_colorAttachmentCount = 0;
-	RenderTargetReference m_depthStencilAttachment;
 };
 
-/// XXX
+/// A non-graphics render pass for RenderGraph.
 class NoGraphicsRenderPassInfo : private RenderPassBase
 {
 private:
@@ -229,34 +193,7 @@ private:
 	}
 };
 
-/// XXX
-class RenderTarget
-{
-	friend class RenderGraphDescription;
-	friend class RenderGraph;
-
-private:
-	TextureInitInfo m_initInfo;
-	TexturePtr m_importedTex;
-	TextureUsageBit m_usage;
-	Array<char, MAX_GR_OBJECT_NAME_LENGTH + 1> m_name;
-
-	void setName(CString name)
-	{
-		const U len = name.getLength();
-		if(len)
-		{
-			ANKI_ASSERT(len <= MAX_GR_OBJECT_NAME_LENGTH);
-			strcpy(&m_name[0], &name[0]);
-		}
-		else
-		{
-			strcpy(&m_name[0], "unnamed");
-		}
-	}
-};
-
-/// XXX
+/// Describes the render graph by creating passes and setting dependencies on them.
 class RenderGraphDescription
 {
 	friend class RenderGraph;
@@ -288,35 +225,72 @@ public:
 		return *pass;
 	}
 
-	/// XXX
-	U32 importRenderTarget(CString name, TexturePtr tex, TextureUsageBit usage)
+	/// Import an existing render target.
+	RenderTargetHandle importRenderTarget(CString name, TexturePtr tex, TextureUsageBit usage)
 	{
-		RenderTarget rt;
+		RT& rt = m_renderTargets.emplaceBack(m_alloc);
 		rt.m_importedTex = tex;
 		rt.m_usage = usage;
 		rt.setName(name);
-		m_renderTargets.emplaceBack(m_alloc, rt);
 		return m_renderTargets.getSize() - 1;
 	}
 
-	/// XXX
-	U32 newRenderTarget(CString name, const TextureInitInfo& initInf)
+	/// Get or create a new render target.
+	RenderTargetHandle newRenderTarget(CString name, const TextureInitInfo& initInf)
 	{
-		RenderTarget rt;
+		RT& rt = m_renderTargets.emplaceBack(m_alloc);
 		rt.m_initInfo = initInf;
 		rt.m_usage = TextureUsageBit::NONE;
 		rt.setName(name);
-		m_renderTargets.emplaceBack(m_alloc, rt);
 		return m_renderTargets.getSize() - 1;
 	}
 
+	/// Import a buffer.
+	RenderPassBufferHandle importBuffer(CString name, BufferPtr buff, BufferUsageBit usage)
+	{
+		Buffer& b = m_buffers.emplaceBack(m_alloc);
+		b.setName(name);
+		b.m_usage = usage;
+		b.m_importedBuff = buff;
+		return m_buffers.getSize() - 1;
+	}
+
 private:
+	class Resource
+	{
+	public:
+		Array<char, MAX_GR_OBJECT_NAME_LENGTH + 1> m_name;
+
+		void setName(CString name)
+		{
+			const U len = name.getLength();
+			ANKI_ASSERT(len <= MAX_GR_OBJECT_NAME_LENGTH);
+			strcpy(&m_name[0], (len) ? &name[0] : "unnamed");
+		}
+	};
+
+	class RT : public Resource
+	{
+	public:
+		TextureInitInfo m_initInfo;
+		TexturePtr m_importedTex;
+		TextureUsageBit m_usage;
+	};
+
+	class Buffer : public Resource
+	{
+	public:
+		BufferUsageBit m_usage;
+		BufferPtr m_importedBuff;
+	};
+
 	StackAllocator<U8> m_alloc;
 	DynamicArray<RenderPassBase*> m_passes;
-	DynamicArray<RenderTarget> m_renderTargets;
+	DynamicArray<RT> m_renderTargets;
+	DynamicArray<Buffer> m_buffers;
 };
 
-/// XXX
+/// Accepts a descriptor of the frame's render passes and sets the dependencies between them.
 class RenderGraph final : public GrObject
 {
 	ANKI_GR_OBJECT
@@ -381,15 +355,17 @@ private:
 	};
 
 	HashMap<TextureInitInfo, RenderTargetCacheEntry> m_renderTargetCache; ///< Non-imported render targets.
-	HashMap<FramebufferInitInfo, FramebufferPtr> m_framebufferCache;
 
 	// Forward declarations
 	class BakeContext;
 	class Pass;
 	class Batch;
 	class RT;
+	class Buffer;
 	class Barrier;
 
+	mutable const BakeContext* m_ctx = nullptr;
+
 	TexturePtr getOrCreateRenderTarget(const TextureInitInfo& initInf);
 
 	static Bool passADependsOnB(BakeContext& ctx, const RenderPassBase& a, const RenderPassBase& b);
@@ -399,8 +375,8 @@ private:
 
 	/// Dump the dependency graph into a file.
 	ANKI_USE_RESULT Error dumpDependencyDotFile(const BakeContext& ctx, CString path) const;
-
-	static StringAuto textureUsageToStr(const BakeContext& ctx, TextureUsageBit usage);
+	static StringAuto textureUsageToStr(StackAllocator<U8>& alloc, TextureUsageBit usage);
+	static StringAuto bufferUsageToStr(StackAllocator<U8>& alloc, BufferUsageBit usage);
 };
 /// @}
 

+ 2 - 1
src/anki/util/DynamicArray.h

@@ -226,11 +226,12 @@ public:
 
 	/// Push back value.
 	template<typename TAllocator, typename... TArgs>
-	void emplaceBack(TAllocator alloc, TArgs&&... args)
+	Value& emplaceBack(TAllocator alloc, TArgs&&... args)
 	{
 		resizeStorage(alloc, m_size + 1);
 		::new(&m_data[m_size]) Value(std::forward<TArgs>(args)...);
 		++m_size;
+		return m_data[m_size - 1];
 	}
 
 	/// Destroy the array.

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

@@ -321,6 +321,13 @@ public:
 	/// Move a StringAuto to this one.
 	String& operator=(StringAuto&& b);
 
+	/// Get a C string.
+	const Char* cstr() const
+	{
+		checkInit();
+		return &m_data[0];
+	}
+
 	/// Return char at the specified position.
 	const Char& operator[](U pos) const
 	{

+ 74 - 9
tests/gr/Gr.cpp

@@ -1541,10 +1541,11 @@ ANKI_TEST(Gr, RenderGraph)
 
 	const U GI_MIP_COUNT = 4;
 
-	TextureInitInfo texInf("SM scratch");
+	TextureInitInfo texInf("dummy tex");
 	texInf.m_width = texInf.m_height = 16;
 	texInf.m_usage = TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE | TextureUsageBit::SAMPLED_FRAGMENT;
 	texInf.m_format = PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM);
+	TexturePtr dummyTex = gr->newInstance<Texture>(texInf);
 
 	// SM
 	RenderTargetHandle smScratchRt = descr.newRenderTarget("SM", texInf);
@@ -1555,7 +1556,7 @@ ANKI_TEST(Gr, RenderGraph)
 	}
 
 	// SM to exponential SM
-	RenderTargetHandle smExpRt = descr.newRenderTarget("ESM", texInf);
+	RenderTargetHandle smExpRt = descr.importRenderTarget("ESM", dummyTex, TextureUsageBit::SAMPLED_FRAGMENT);
 	{
 		GraphicsRenderPassInfo& pass = descr.newGraphicsRenderPass("ESM");
 		pass.newConsumer({smScratchRt, TextureUsageBit::SAMPLED_FRAGMENT});
@@ -1579,7 +1580,7 @@ ANKI_TEST(Gr, RenderGraph)
 	}
 
 	// GI light
-	RenderTargetHandle giGiLightRt = descr.newRenderTarget("GI light", texInf);
+	RenderTargetHandle giGiLightRt = descr.importRenderTarget("GI light", dummyTex, TextureUsageBit::SAMPLED_FRAGMENT);
 	for(U faceIdx = 0; faceIdx < 6; ++faceIdx)
 	{
 		GraphicsRenderPassInfo& pass =
@@ -1646,23 +1647,87 @@ ANKI_TEST(Gr, RenderGraph)
 		pass.newProducer({quarterDepthRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
 	}
 
-#if 0
-	// Light
-	RenderTargetHandle lightRt = descr.newRenderTarget("Light", texInf);
+	// SSAO
+	RenderTargetHandle ssaoRt = descr.newRenderTarget("SSAO", texInf);
+	{
+		GraphicsRenderPassInfo& pass = descr.newGraphicsRenderPass("SSAO main");
+		pass.newConsumer({ssaoRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newConsumer({quarterDepthRt, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newConsumer({gbuffRt2, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newProducer({ssaoRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+
+		RenderTargetHandle ssaoVBlurRt = descr.newRenderTarget("SSAO tmp", texInf);
+		GraphicsRenderPassInfo& pass2 = descr.newGraphicsRenderPass("SSAO vblur");
+		pass2.newConsumer({ssaoRt, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass2.newConsumer({ssaoVBlurRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass2.newProducer({ssaoVBlurRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+
+		GraphicsRenderPassInfo& pass3 = descr.newGraphicsRenderPass("SSAO hblur");
+		pass3.newConsumer({ssaoRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass3.newProducer({ssaoRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass3.newConsumer({ssaoVBlurRt, TextureUsageBit::SAMPLED_FRAGMENT});
+	}
+
+	// Volumetric
+	RenderTargetHandle volRt = descr.newRenderTarget("Vol", texInf);
+	{
+		GraphicsRenderPassInfo& pass = descr.newGraphicsRenderPass("Vol main");
+		pass.newConsumer({volRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newConsumer({quarterDepthRt, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newProducer({volRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+
+		RenderTargetHandle volVBlurRt = descr.newRenderTarget("Vol tmp", texInf);
+		GraphicsRenderPassInfo& pass2 = descr.newGraphicsRenderPass("Vol vblur");
+		pass2.newConsumer({volRt, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass2.newConsumer({volVBlurRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass2.newProducer({volVBlurRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+
+		GraphicsRenderPassInfo& pass3 = descr.newGraphicsRenderPass("Vol hblur");
+		pass3.newConsumer({volRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass3.newProducer({volRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass3.newConsumer({volVBlurRt, TextureUsageBit::SAMPLED_FRAGMENT});
+	}
+
+	// Forward shading
+	RenderTargetHandle fsRt = descr.newRenderTarget("FS", texInf);
+	{
+		GraphicsRenderPassInfo& pass = descr.newGraphicsRenderPass("Forward shading");
+		pass.newConsumer({fsRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newProducer({fsRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newConsumer(
+			{halfDepthRt, TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ});
+		pass.newConsumer({volRt, TextureUsageBit::SAMPLED_FRAGMENT});
+	}
+
+	// Light shading
+	RenderTargetHandle lightRt = descr.importRenderTarget("Light", dummyTex, TextureUsageBit::NONE);
 	{
 		GraphicsRenderPassInfo& pass = descr.newGraphicsRenderPass("Light shading");
 
-		pass.newConsumer({lightRt, TextureUsageBit::NONE});
+		pass.newConsumer({lightRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
 		pass.newConsumer({gbuffRt0, TextureUsageBit::SAMPLED_FRAGMENT});
 		pass.newConsumer({gbuffRt1, TextureUsageBit::SAMPLED_FRAGMENT});
 		pass.newConsumer({gbuffRt2, TextureUsageBit::SAMPLED_FRAGMENT});
 		pass.newConsumer({gbuffDepth, TextureUsageBit::SAMPLED_FRAGMENT});
 		pass.newConsumer({smExpRt, TextureUsageBit::SAMPLED_FRAGMENT});
 		pass.newConsumer({giGiLightRt, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newConsumer({ssaoRt, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newConsumer({fsRt, TextureUsageBit::SAMPLED_FRAGMENT});
 
-		pass.newProducer({giGiLightRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newProducer({lightRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+	}
+
+	// TAA
+	RenderTargetHandle taaHistoryRt = descr.importRenderTarget("TAA hist", dummyTex, TextureUsageBit::SAMPLED_FRAGMENT);
+	RenderTargetHandle taaRt = descr.importRenderTarget("TAA", dummyTex, TextureUsageBit::NONE);
+	{
+		GraphicsRenderPassInfo& pass = descr.newGraphicsRenderPass("Temporal AA");
+
+		pass.newConsumer({lightRt, TextureUsageBit::SAMPLED_FRAGMENT});
+		pass.newConsumer({taaRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newProducer({taaRt, TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE});
+		pass.newConsumer({taaHistoryRt, TextureUsageBit::SAMPLED_FRAGMENT});
 	}
-#endif
 
 	rgraph->compileNewGraph(descr);
 	COMMON_END()